/* * Ivy, C interface * * Copyright 1997-1999 * Centre d'Etudes de la Navigation Aerienne * * Sockets * * Authors: Francois-Regis Colin * * $Id$ * * Please refer to file version.h for the * copyright notice regarding this software */ #ifdef WIN32 #include #endif #include #include #include #include #include #ifdef WIN32 #define close closesocket /*#define perror (a ) printf(a" error=%d\n",WSAGetLastError());*/ #else #include #include #include #include #include #include #include #include #endif #include "list.h" #include "ivychannel.h" #include "ivysocket.h" #include "ivyloop.h" #define MAX_BUFFER 2048 struct _server { Server next; HANDLE fd; Channel channel; unsigned short port; void *(*create)(Client client); void (*handle_delete)(Client client, void *data); SocketInterpretation interpretation; }; struct _client { Client next; HANDLE fd; Channel channel; unsigned short port; struct sockaddr_in from; SocketInterpretation interpretation; void (*handle_delete)(Client client, void *data); char buffer[MAX_BUFFER+2]; char *ptr; void *data; }; static Server servers_list = NULL; static Client clients_list = NULL; #ifdef WIN32 WSADATA WsaData; #endif void SocketInit() { if (! channel_init ) { fprintf (stderr, "Channel management functions not set, exiting.\n"); exit(-1); } (*channel_init)(); } static void DeleteSocket(void *data) { Client client = (Client )data; if (client->handle_delete ) (*client->handle_delete) (client, client->data ); shutdown (client->fd, 2 ); close (client->fd ); LIST_REMOVE (clients_list, client ); } static void DeleteServerSocket(void *data) { Server server = (Server )data; #ifdef BUGGY_END if (server->handle_delete ) (*server->handle_delete) (server, NULL ); #endif shutdown (server->fd, 2 ); close (server->fd ); LIST_REMOVE (servers_list, server); } static void HandleSocket (Channel channel, HANDLE fd, void *data) { Client client = (Client)data; char *ptr; char *ptr_nl; long nb_to_read = 0; long nb; int len; /* limitation taille buffer */ nb_to_read = MAX_BUFFER - (client->ptr - client->buffer ); if (nb_to_read == 0 ) { fprintf(stderr, "Erreur message trop long sans LF\n"); client->ptr = client->buffer; return; } len = sizeof (client->from ); nb = recvfrom (fd, client->ptr, nb_to_read,0,(struct sockaddr *)&client->from,&len); if (nb < 0) { perror(" Read Socket "); (*channel_close) (client->channel ); return; } if (nb == 0 ) { (*channel_close) (client->channel ); return; } client->ptr += nb; *(client->ptr) = '\0'; ptr = client->buffer; while ((ptr_nl = strchr (ptr, '\n' ))) { *ptr_nl = '\0'; if (client->interpretation ) (*client->interpretation) (client, client->data, ptr ); else fprintf (stderr,"Socket No interpretation function ???!\n"); ptr = ++ptr_nl; } if (*ptr != '\0' ) { /* recopie ligne incomplete au debut du buffer */ strcpy (client->buffer, ptr ); client->ptr = client->buffer + strlen(client->buffer); } else { client->ptr = client->buffer; } } static void HandleServer(Channel channel, HANDLE fd, void *data) { Server server = (Server ) data; Client client; HANDLE ns; int addrlen; struct sockaddr_in remote2; addrlen = sizeof (remote2 ); if ((ns = accept (fd, (struct sockaddr *)&remote2, &addrlen)) <0) { perror ("*** accept ***"); return; }; LIST_ADD (clients_list, client ); if (!client ) { fprintf(stderr,"NOK Memory Alloc Error\n"); close (fd ); exit(0); } client->from = remote2; client->fd = ns; client->channel = (*channel_setup) (ns, client, DeleteSocket, HandleSocket ); client->interpretation = server->interpretation; client->ptr = client->buffer; client->handle_delete = server->handle_delete; client->data = (*server->create) (client ); } Server SocketServer(unsigned short port, void*(*create)(Client client), void(*handle_delete)(Client client, void *data), void(*interpretation) (Client client, void *data, char *ligne)) { Server server; HANDLE fd; int one=1; struct sockaddr_in local; int addrlen; if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0){ perror ("***open socket ***"); exit(0); }; local.sin_family = AF_INET; local.sin_addr.s_addr = INADDR_ANY; local.sin_port = htons (port); if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*)&one,sizeof(one)) < 0) { perror ("*** set socket option SO_REUSEADDR ***"); exit(0); } #ifdef SO_REUSEPORT if (setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (char *)&one, sizeof (one)) < 0) { perror ("*** set socket option REUSEPORT ***"); exit(0); } #endif if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) { perror ("*** bind ***"); exit(0); } addrlen = sizeof (local ); if (getsockname(fd,(struct sockaddr *)&local, &addrlen) < 0) { perror ("***get socket name ***"); exit(0); } if (listen (fd, 128) < 0){ perror ("*** listen ***"); exit(0); }; LIST_ADD (servers_list, server ); if (!server ) { fprintf(stderr,"NOK Memory Alloc Error\n"); exit(0); } server->fd = fd; server->channel = (*channel_setup) (fd, server, DeleteServerSocket, HandleServer ); server->create = create; server->handle_delete = handle_delete; server->interpretation = interpretation; server->port = ntohs(local.sin_port); return server; } unsigned short SocketServerGetPort (Server server ) { return server ? server->port : 0; } void SocketServerClose (Server server ) { if (!server) return; (*channel_close) (server->channel ); } char *SocketGetPeerHost (Client client ) { int err; struct sockaddr_in name; struct hostent *host; int len = sizeof(name); if (!client) return "undefined"; err = getpeername (client->fd, (struct sockaddr *)&name, &len ); if (err < 0 ) return "can't get peer"; host = gethostbyaddr ((char *)&name.sin_addr.s_addr,sizeof(name.sin_addr.s_addr),name.sin_family); if (host == NULL ) return "can't translate addr"; return host->h_name; } struct in_addr * SocketGetRemoteAddr (Client client ) { return client ? &client->from.sin_addr : 0; } void SocketGetRemoteHost (Client client, char **host, unsigned short *port ) { struct hostent *hostp; if (!client) return; /* extract hostname and port from last message received */ hostp = gethostbyaddr ((char *)&client->from.sin_addr.s_addr, sizeof(client->from.sin_addr.s_addr),client->from.sin_family); if (hostp == NULL ) *host = "unknown"; else *host = hostp->h_name; *port = ntohs (client->from.sin_port ); } void SocketClose (Client client ) { if (client) (*channel_close) (client->channel ); } void SocketSendRaw (Client client, char *buffer, int len ) { int err; if (!client) return; err = send (client->fd, buffer, len, 0 ); if (err != len ) perror ("*** send ***"); } void SocketSetData (Client client, void *data ) { if (client) client->data = data; } void SocketSend (Client client, char *fmt, ... ) { char buffer[4096]; va_list ap; int len; if (!client) return; va_start (ap, fmt ); len = vsprintf (buffer, fmt, ap ); SocketSendRaw (client, buffer, len ); va_end (ap ); } void *SocketGetData (Client client ) { return client ? client->data : 0; } void SocketBroadcast ( char *fmt, ... ) { Client client; char buffer[4096]; va_list ap; int len; va_start (ap, fmt ); len = vsprintf (buffer, fmt, ap ); va_end (ap ); LIST_EACH (clients_list, client ) { SocketSendRaw (client, buffer, len ); } } /* Ouverture d'un canal TCP/IP en mode client */ Client SocketConnect (char * host, unsigned short port, void *data, SocketInterpretation interpretation, void (*handle_delete)(Client client, void *data) ) { struct hostent *rhost; if ((rhost = gethostbyname (host )) == NULL) { fprintf(stderr, "Erreur %s Calculateur inconnu !\n",host); return NULL; } return SocketConnectAddr ((struct in_addr*)(rhost->h_addr), port, data, interpretation, handle_delete); } Client SocketConnectAddr (struct in_addr * addr, unsigned short port, void *data, SocketInterpretation interpretation, void (*handle_delete)(Client client, void *data) ) { HANDLE handle; Client client; struct sockaddr_in remote; remote.sin_family = AF_INET; remote.sin_addr = *addr; remote.sin_port = htons (port); if ((handle = socket (AF_INET, SOCK_STREAM, 0)) < 0){ perror ("*** client socket ***"); return NULL; }; if (connect (handle, (struct sockaddr *)&remote, sizeof(remote) ) < 0){ perror ("*** client connect ***"); return NULL; }; LIST_ADD (clients_list, client ); if (!client ) { fprintf(stderr,"NOK Memory Alloc Error\n"); close (handle ); exit(0); } client->fd = handle; client->channel = (*channel_setup) (handle, client, DeleteSocket, HandleSocket ); client->interpretation = interpretation; client->ptr = client->buffer; client->data = data; client->handle_delete = handle_delete; client->from.sin_family = AF_INET; client->from.sin_addr = *addr; client->from.sin_port = htons (port); return client; } int SocketWaitForReply (Client client, char *buffer, int size, int delai) { fd_set rdset; struct timeval timeout; struct timeval *timeoutptr = &timeout; int ready; char *ptr; char *ptr_nl; long nb_to_read = 0; long nb; HANDLE fd; fd = client->fd; ptr = buffer; timeout.tv_sec = delai; timeout.tv_usec = 0; do { /* limitation taille buffer */ nb_to_read = size - (ptr - buffer ); if (nb_to_read == 0 ) { fprintf(stderr, "Erreur message trop long sans LF\n"); ptr = buffer; return -1; } FD_ZERO (&rdset ); FD_SET (fd, &rdset ); ready = select(fd+1, &rdset, 0, 0, timeoutptr); if (ready < 0 ) { perror("select"); return -1; } if (ready == 0 ) { return -2; } if ((nb = recv (fd , ptr, nb_to_read, 0 )) < 0) { perror(" Read Socket "); return -1; } if (nb == 0 ) return 0; ptr += nb; *ptr = '\0'; ptr_nl = strchr (buffer, '\n' ); } while (!ptr_nl ); *ptr_nl = '\0'; return (ptr_nl - buffer); } /* Socket UDP */ Client SocketBroadcastCreate (unsigned short port, void *data, SocketInterpretation interpretation ) { HANDLE handle; Client client; struct sockaddr_in local; int on = 1; local.sin_family = AF_INET; local.sin_addr.s_addr = INADDR_ANY; local.sin_port = htons (port); if ((handle = socket (AF_INET, SOCK_DGRAM, 0)) < 0){ perror ("*** dgram socket ***"); return NULL; }; /* wee need to used multiple client on the same host */ if (setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) { perror ("*** set socket option REUSEADDR ***"); return NULL; }; #ifdef SO_REUSEPORT if (setsockopt (handle, SOL_SOCKET, SO_REUSEPORT, (char *)&on, sizeof (on)) < 0) { perror ("*** set socket option REUSEPORT ***"); return NULL; } #endif /* wee need to broadcast */ if (setsockopt (handle, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof (on)) < 0) { perror ("*** BROADCAST ***"); return NULL; }; if (bind(handle, (struct sockaddr *)&local, sizeof(local)) < 0) { perror ("*** test BIND ***"); return NULL; }; LIST_ADD(clients_list, client ); if (!client ) { fprintf(stderr,"NOK Memory Alloc Error\n"); close (handle ); exit(0); } client->fd = handle; client->channel = (*channel_setup) (handle, client, DeleteSocket, HandleSocket ); client->interpretation = interpretation; client->ptr = client->buffer; client->data = data; return client; } void SocketSendBroadcast (Client client, unsigned long host, unsigned short port, char *fmt, ... ) { struct sockaddr_in remote; char buffer[4096]; va_list ap; int err,len; if (!client) return; va_start (ap, fmt ); len = vsprintf (buffer, fmt, ap ); /* Send UDP packet to the dest */ remote.sin_family = AF_INET; remote.sin_addr.s_addr = htonl (host ); remote.sin_port = htons(port); err = sendto (client->fd, buffer, len,0, (struct sockaddr *)&remote,sizeof(remote)); if (err != len) { perror ("*** send ***"); } va_end (ap ); }