/* vim:ts=8:sts=8:sw=4:noai:noexpandtab * * Dump PGM packets to the console. * * Copyright (c) 2006-2007 Miru Limited. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #ifdef G_OS_UNIX # include # include # include # include #endif #include /* PGM internals */ #include /* example dependencies */ #include #include /* globals */ static const char* g_network = "239.192.0.1"; static GIOChannel* g_io_channel = NULL; static GMainLoop* g_loop = NULL; static void on_signal (int); static gboolean on_startup (gpointer); static gboolean on_mark (gpointer); static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[] ) { GError* err = NULL; setlocale (LC_ALL, ""); log_init (); g_message ("pgmdump"); /* setup signal handlers */ signal (SIGSEGV, on_sigsegv); signal (SIGINT, on_signal); signal (SIGTERM, on_signal); #ifdef SIGHUP signal (SIGHUP, SIG_IGN); #endif /* delayed startup */ g_message ("scheduling startup."); g_timeout_add (0, (GSourceFunc)on_startup, NULL); /* dispatch loop */ g_loop = g_main_loop_new (NULL, FALSE); g_message ("entering main event loop ... "); g_main_loop_run (g_loop); g_message ("event loop terminated, cleaning up."); /* cleanup */ g_main_loop_unref (g_loop); g_loop = NULL; if (g_io_channel) { g_message ("closing socket."); g_io_channel_shutdown (g_io_channel, FALSE, &err); g_io_channel = NULL; } g_message ("finished."); return EXIT_SUCCESS; } static void on_signal ( G_GNUC_UNUSED int signum ) { puts ("on_signal"); g_main_loop_quit(g_loop); } static gboolean on_startup ( G_GNUC_UNUSED gpointer data ) { int e; g_message ("startup."); /* find PGM protocol id */ // TODO: fix valgrind errors int ipproto_pgm = IPPROTO_PGM; #ifdef HAVE_GETPROTOBYNAME_R char b[1024]; struct protoent protobuf, *proto; e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); if (e != -1 && proto != NULL) { if (proto->p_proto != ipproto_pgm) { g_message ("Setting PGM protocol number to %i from /etc/protocols.\n"); ipproto_pgm = proto->p_proto; } } #else struct protoent *proto = getprotobyname ("pgm"); if (proto != NULL) { if (proto->p_proto != ipproto_pgm) { g_message ("Setting PGM protocol number to %i from /etc/protocols.\n", proto ->p_proto); ipproto_pgm = proto->p_proto; } } #endif /* open socket for snooping */ g_message ("opening raw socket."); int sock = socket (PF_INET, SOCK_RAW, ipproto_pgm); if (sock < 0) { perror("on_startup() failed"); #ifdef G_OS_UNIX if (EPERM == errno && 0 != getuid()) { g_message ("PGM protocol requires this program to run as superuser."); } #endif g_main_loop_quit (g_loop); return FALSE; } #ifdef G_OS_UNIX /* drop out of setuid 0 */ if (0 == getuid ()) { g_message ("dropping superuser privileges."); setuid ((gid_t)65534); setgid ((uid_t)65534); } #endif char _t = 1; e = setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); if (e < 0) { perror ("on_startup() failed"); close (sock); g_main_loop_quit (g_loop); return FALSE; } /* buffers */ int buffer_size = 0; socklen_t len = 0; e = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char*)&buffer_size, &len); if (e == 0) { g_message ("receive buffer set at %i bytes.\n", buffer_size); } e = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char*)&buffer_size, &len); if (e == 0) { g_message ("send buffer set at %i bytes.\n", buffer_size); } /* bind */ struct sockaddr_in addr; memset (&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); e = bind (sock, (struct sockaddr*)&addr, sizeof(addr)); if (e < 0) { perror ("on_startup() failed"); close (sock); g_main_loop_quit (g_loop); return FALSE; } /* multicast */ struct ip_mreq mreq; memset (&mreq, 0, sizeof(mreq)); mreq.imr_interface.s_addr = htonl (INADDR_ANY); g_message ("listening on interface %s.\n", inet_ntoa (mreq.imr_interface)); mreq.imr_multiaddr.s_addr = inet_addr (g_network); g_message ("subscription on multicast address %s.\n", inet_ntoa (mreq.imr_multiaddr)); e = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); if (e < 0) { perror ("on_startup() failed"); close (sock); g_main_loop_quit (g_loop); return FALSE; } /* multicast loopback */ /* multicast ttl */ /* add socket to event manager */ g_io_channel = g_io_channel_unix_new (sock); g_message ("socket opened with encoding %s.\n", g_io_channel_get_encoding (g_io_channel)); /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); /* period timer to indicate some form of life */ // TODO: Gnome 2.14: replace with g_timeout_add_seconds() g_timeout_add (10 * 1000, (GSourceFunc)on_mark, NULL); g_message ("startup complete."); return FALSE; } static gboolean on_mark ( G_GNUC_UNUSED gpointer data ) { g_message ("-- MARK --"); return TRUE; } static gboolean on_io_data ( GIOChannel* source, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer data ) { char buffer[4096]; int fd = g_io_channel_unix_get_fd (source); struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); int len = recvfrom (fd, buffer, sizeof(buffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); g_message ("%i bytes received from %s.\n", len, inet_ntoa (addr.sin_addr)); if (!pgm_print_packet (buffer, len)) { g_message ("invalid packet :("); } fflush (stdout); return TRUE; } /* eof */