/* * $Id: udpserv.c,v 1.5 2002/10/02 13:57:00 kingofgib Exp $ * * A fairly advanced UPD server. We start up, loop through all interfaces * we find, and bind to each of their addresses, including loopback and * broadcast. When a datagram arrives, the server prints the address that * it was sent to. * The port we listen to is currently hard-coded at 40000. If you feel like * it, make it configurable. */ # include # include # include # include # include /* * We maintain a linked list of sockets, so that we can create an arbitrary * number of sockets. Then, at exit time, we can free the whole lot of * them in one swoop. */ typedef struct sock_item { PNETSOCK ps; struct sock_item * next; } sock_item; static sock_item* skts; /* Work pointer for sockets list */ static sock_item* phead; /* Head of sockets list */ /* * Allocate a new sock_item, and link it up to the previous one. If the * previous one is NULL, then this is the first item, so remember it as * the head of the list (need it later, in free_sock_item(). */ static struct sock_item* new_sock_item( struct sock_item * prev, int family ) { struct sock_item * n; if ( !(n = (struct sock_item*) malloc( sizeof( struct sock_item ) )) ) return NULL; n->ps = pnetUDPSocket2( family ); n->next = NULL; /* * If previous is given, link it up to this one, else set the head * of the list phead */ if ( prev ) prev->next = n; else phead = n; return n; } /* * Free a linked list of sock_items */ static void free_sock_item( struct sock_item * fst ) { struct sock_item * tmp; tmp = fst; for( ; fst; ) { tmp = fst; fst = fst->next; pnetClose( tmp->ps ); free( tmp ); } } static void sig_handler( int sig ) { if ( sig != SIGINT ) return ; free_sock_item( phead ); exit( 0 ); } /* * The read callback. */ static int read_callback( PNETSOCK ps, void * data ) { int ret; char buf[128]; /* * We do a timed read with a value of 0 milliseconds. Effectively, this * will cause read to return immediately. This should be OK, since once * the read_callback is invoked, there has to be input data on a UDP socket. */ switch( (ret = pnetReadTimed( ps, buf, sizeof( buf ), 0, 0 )) ) { case 0: printf("Connection closed\n"); /* Never happens for UDP */ return 0; case PNET_READ_ERROR: printf("Input error\n"); return -1; case PNET_READ_TIMED_OUT: printf("Timed out\n"); /* Never happens for UDP */ return -1; default: break; } printf("Got input on local address %s\n", data ? (char*)data : "?" ); return 0; } int main( int argc, char ** argv ) { PNETIF pif; /* Head of inteface list */ PNETIFADDR pia; /* Interface address pointer */ int fam; /* Address family to use: PNET_IPv4 or IPv6 */ char buf[PNET_ADDR_STRLEN]; /* To print addresses we find */ char bbuf[32]; /* For broadcast addresses */ fam = 0; skts= NULL; /* Install our sighandler for SIGINT */ pnetSetSighandler( SIGINT, sig_handler ); /* * 1. Get information on all intefaces. We pass PNET_UNSPEC to indicate * that we want information on all interfaces, not just the IPv4 or IPv6 * enabled ones. */ pif = pnetGetIfInfo( PNET_UNSPEC ); if ( !pif ) { fprintf( stderr, "Cannot find any network intefaces\n" ); return 1; } for ( ; pif; pif = pnetIfNext( pif ) ) { PNETADDRSTORAGE pas; PNETADDR pa = PNET_SADDR( pas ); printf( "Trying interface %s ...", pnetIfName( pif ) ); if ( ! pnetIfIsUp( pif ) ) { printf(" not up, skipping\n"); continue; } printf(" is up\n" ); /* * 2. Loop through the interface's addresses and bind a socket * to each of them. */ for ( pia = pnetIfGetNextAddr( pif, NULL ); pia; pia = pnetIfGetNextAddr( pif, pia ) ) { const pnet_byte* pbin; pnetIfAddrToString( pia, buf, sizeof( buf ) ); fam = pnetIfAddrGetFamily( pia ); printf(" binding to %s (%s)\n", buf, fam == PNET_IPv4 ? "ipv4" : "ipv6" ); /* Convert interface address to an internet address */ pnetIfAddrToInetAddr( pia, pa ); /* Set port to listen to */ pnetAddrSetPort( pa, 40000 ); /* Create socket, and bind the address to it */ skts = new_sock_item( skts, fam ); pnetListenAtAddress( skts->ps, pa ); pnetSockAddReadcallback( skts->ps, read_callback, strdup( pnetAddrToString( pa, buf, sizeof( buf ) ) ) ); /* * If address has a broadcast address, bind to it as well. * Read address, copy into a string, and pass that to * pnetListenAt() */ if ( (pbin = pnetIfAddrGetBroadcast( pia )) ) { sprintf( bbuf,"%d.%d.%d.%d", pbin[0],pbin[1],pbin[2],pbin[3]); printf(" binding to %s (broadcast)\n", bbuf); skts = new_sock_item( skts, fam ); pnetListenAt( skts->ps, bbuf, "40000" ); pnetSockAddReadcallback( skts->ps, read_callback, strdup( bbuf ) ); } } } /* * Finally, bind the limited broadcast address. This might fail on * some hosts, so we ignore the return status */ skts = new_sock_item( skts, PNET_IPv4 ); printf( "Binding to 255.255.255.255 (limited broadcast)\n"); pnetListenAt( skts->ps, "255.255.255.255", "40000" ); pnetSockAddReadcallback( skts->ps, read_callback,(void*)"255.255.255.255" ); /* Detach the process if so desired */ if ( argc == 2 && ! strcmp( argv[1], "-d" ) ) pnetDetachProcess( "udp6-server", "/" ); pnetStartListen( PNET_LISTEN_SELF ); return 0; }