src/network.c

Go to the documentation of this file.
00001 /*
00002  * network.c
00003  */
00004 
00005 #include "copyright.h"
00006 #include "config.h"
00007 
00008 #include <sys/types.h>
00009 #include <sys/stat.h>
00010 #include <unistd.h>
00011 #include <sys/file.h>
00012 #include <sys/ioctl.h>
00013 #include <sys/wait.h>
00014 #include <signal.h>
00015 #include <errno.h>
00016 #include <netdb.h>
00017 #include <arpa/inet.h>
00018 #include "externs.h"
00019 
00020 struct listening_socket_t {
00021         int fd;
00022         struct sockaddr_storage addr;
00023         int salen;
00024         struct event ev;
00025         struct listening_socket_t *next;
00026 } *listening_sockets = NULL;
00027 
00028 DESC *descriptor_list = NULL;
00029 
00030 int network_init(int port);
00031 
00032 static void network_accept_client(int fd, short event, void *arg);
00033 static int network_bind_port(struct sockaddr *addr, int salen);
00034 static void network_make_nonblocking(int socket);
00035 static void network_make_blocking(int socket);
00036 DESC *network_initialize_socket(int socket, struct sockaddr *saddr,
00037                                                                 int addrlen);
00038 
00039 static int network_bind_port(struct sockaddr *addr, int salen)
00040 {
00041         int one = 1;
00042         struct listening_socket_t *lst = NULL;
00043 
00044         lst = malloc(sizeof(struct listening_socket_t));
00045         memset(lst, 0, sizeof(struct listening_socket_t));
00046 
00047         lst->fd = socket(addr->sa_family, SOCK_STREAM, 0);
00048         if(lst->fd < 0) {
00049                 log_perror("NET", "FAIL", "network_bind_port", "socket");
00050                 goto error;
00051         }
00052 
00053         if(setsockopt(lst->fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
00054                 log_perror("NET", "WARN", "network_bind_port", "setsockopt");
00055         }
00056 
00057         memcpy(&lst->addr, addr, salen);
00058         lst->salen = salen;
00059 
00060         if(bind(lst->fd, (struct sockaddr *) &lst->addr, lst->salen) < 0) {
00061                 log_perror("NET", "FAIL", "network_bind_port", "bind");
00062                 goto error;
00063         }
00064 
00065         if(listen(lst->fd, 0) < 0) {
00066                 log_perror("NET", "FAIL", "network_bind_port", "listen");
00067                 goto error;
00068         }
00069 
00070         network_make_nonblocking(lst->fd);
00071 
00072         event_set(&lst->ev, lst->fd, EV_READ | EV_PERSIST, network_accept_client,
00073                           lst);
00074         event_add(&lst->ev, NULL);
00075 
00076         lst->next = listening_sockets;
00077         listening_sockets = lst;
00078         return 1;
00079 
00080   error:
00081         if(lst->fd >= 0) {
00082                 close(lst->fd);
00083         }
00084         memset(lst, 0, sizeof(struct listening_socket_t));
00085         return 0;
00086 }
00087 
00088 int network_init(int port)
00089 {
00090         struct addrinfo hints;
00091         struct addrinfo *res, *walk;
00092         char myservice[128];
00093         char hostname[1025];
00094         int error;
00095 
00096         snprintf(myservice, 32, "%d", port + 10);
00097 
00098         signal(SIGPIPE, SIG_IGN);
00099 
00100         memset(&hints, 0, sizeof(hints));
00101         hints.ai_family = AF_UNSPEC;
00102         hints.ai_flags =
00103                 AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED;
00104         hints.ai_socktype = SOCK_STREAM;
00105         error = getaddrinfo(NULL, myservice, &hints, &res);
00106         walk = res;
00107         if(error)
00108                 perror(gai_strerror(error));
00109         else {
00110                 /*
00111                  * "res" has a chain of addrinfo structure filled with
00112                  * 0.0.0.0 (for IPv4), 0:0:0:0:0:0:0:0 (for IPv6) and alike,
00113                  * with port filled for "myservice".
00114                  */
00115                 while (walk) {
00116                         if(getnameinfo(walk->ai_addr, walk->ai_addrlen, hostname, 1025,
00117                                                    myservice, 128, NI_NUMERICSERV)) {
00118                                 log_printf("network] serious problem in getnameinfo().");
00119                         }
00120                         if(network_bind_port(walk->ai_addr, walk->ai_addrlen) == 0) {
00121                                 log_printf("network] binding to %s %s %s failed.",
00122                                                    (walk->ai_family == AF_INET6 ? "IPv6" : "IPv4"),
00123                                                    hostname, myservice);
00124                         } else {
00125                                 log_printf("network] bound to %s %s %s. ",
00126                                                    (walk->ai_family == AF_INET6 ? "IPv6" : "IPv4"),
00127                                                    hostname, myservice);
00128                         }
00129 
00130                         walk = walk->ai_next;
00131                 }
00132         }
00133         freeaddrinfo(res);
00134         return 1;
00135 }
00136 
00137 static void network_make_nonblocking(int s)
00138 {
00139         long flags = 0;
00140 
00141         if(fcntl(s, F_GETFL, &flags) < 0) {
00142                 log_perror("NET", "FAIL", "make_nonblocking", "fcntl F_GETFL");
00143         }
00144         flags |= O_NONBLOCK;
00145         if(fcntl(s, F_SETFL, flags) < 0) {
00146                 log_perror("NET", "FAIL", "make_nonblocking", "fcntl F_SETFL");
00147         }
00148         flags = 1;
00149         if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags)) < 0) {
00150                 log_perror("NET", "FAIL", "make_nonblocking", "setsockopt NDELAY");
00151         }
00152 }
00153 
00154 static void network_make_blocking(int s)
00155 {
00156         long flags = 0;
00157 
00158         if(fcntl(s, F_GETFL, &flags) < 0) {
00159                 log_perror("NET", "FAIL", "make_blocking", "fcntl F_GETFL");
00160         }
00161         flags &= ~O_NONBLOCK;
00162         if(fcntl(s, F_SETFL, flags) < 0) {
00163                 log_perror("NET", "FAIL", "make_blocking", "fcntl F_SETFL");
00164         }
00165         flags = 0;
00166         if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags)) < 0) {
00167                 log_perror("NET", "FAIL", "make_blocking", "setsockopt NDELAY");
00168         }
00169 }
00170 
00171 /* choke_player: cork the player's sockets, must have a matching release_socket */
00172 void network_choke_socket(DESC * desc)
00173 {
00174         int eins = 1;
00175 
00176         if(desc->chokes == 0) {
00177 #if defined(TCP_CORK)                   // Linux 2.4, 2.6
00178                 setsockopt(d->fd, IPPROTO_TCP, TCP_CORK, &eins, sizeof(eins));
00179 #elif defined(TCP_NOPUSH)               // *BSD, Mac OS X
00180                 setsockopt(d->fd, IPPROTO_TCP, TCP_NOPUSH, &eins, sizeof(eins));
00181 #else // else
00182                 /* Nothing! */
00183 #endif
00184         }
00185         desc->chokes++;
00186 }
00187 
00188 void network_release_socket(DESC * desc)
00189 {
00190         int null = 0;
00191 
00192         desc->chokes--;
00193         if(desc->chokes < 0)
00194                 desc->chokes = 0;
00195 
00196         if(desc->chokes == 0) {
00197 #if defined(TCP_CORK)                   // Linux 2.4, 2.6
00198                 setsockopt(d->fd, IPPROTO_TCP, TCP_CORK, &null, sizeof(null));
00199 #elif defined(TCP_NOPUSH)               // *BSD, Mac OS X
00200                 setsockopt(d->fd, IPPROTO_TCP, TCP_NOPUSH, &null, sizeof(null));
00201 #else // else
00202                 /* Nothing! */
00203 #endif
00204         }
00205 }
00206 
00207 static void network_accept_client(int fd, short event, void *arg)
00208 {
00209         int newfd;
00210         struct sockaddr_storage addr;
00211         unsigned int addrlen = sizeof(struct sockaddr_storage);
00212         struct descriptor_data *desc;
00213 
00214         newfd = accept(fd, (struct sockaddr *) addr, &addrlen);
00215         if(newfd < 0) {
00216                 log_printf("network] accept on %d failed with '%s'", fd,
00217                                    strerror(errno));
00218                 return;
00219         }
00220         desc = network_initialize_socket(newfd, addr, addrlen);
00221         log_printf("network] connection from %s %d", desc->ip, desc->port);
00222 }
00223 
00224 DESC *network_initialize_socket(int socket, struct sockaddr * addr,
00225                                                                 int addrlen)
00226 {
00227         char remotehost[NETWORK_HOSTNAME_MAX];
00228         char remoteport[NETWORK_PORTNAME_MAX];
00229         DESC *desc;
00230 
00231         desc = malloc(sizeof(DESC));
00232         memset(desc, 0, sizeof(DESC));
00233 
00234         desc->fd = socket;
00235 
00236         memcpy(desc->saddr, addr, addrlen);
00237 
00238         if(getnameinfo(addr, addrlen, remotehost, NETWORK_HOSTNAME_MAX,
00239                                    remoteport, NETWORK_PORTNAME_MAX,
00240                                    NI_NUMERICSERV | NI_NUMERICHOST)) {
00241                 log_printf("network] getnameinfo failed!");
00242                 desc->ip = strdup("unspec");
00243                 desc->port = -1;
00244         } else {
00245                 desc->ip = strdup(remotehost);
00246                 desc->port = itoa(remoteport);
00247         }
00248 
00249         network_make_nonblocking(socket);
00250 
00251         desc->connected_at = mudstate.now;
00252         desc->retries_left = mudconf.retry_limit;
00253         desc->timeout = mudconf.idle_timeout;
00254         desc->host_info = 1;
00255         desc->next = descriptor_list;
00256         descriptor->list = desc;
00257 
00258         desc->sock_buff = bufferevent_new(desc->fd, network_write_callback,
00259                                                                           network_read_callback,
00260                                                                           network_error_callback, NULL);
00261         bufferevent_disable(desc->sock_buff, EV_READ);
00262         bufferevent_enable(desc->sock_buff, EV_WRITE);
00263         event_set(&desc->sock_ev, desc->fd, EV_READ | EV_PERSIST,
00264                           network_accept_input, d);
00265         event_add(&desc->sock_ev, NULL);
00266         telnet_init(desc);
00267 
00268         welcome_user(desc);
00269 
00270         return desc;
00271 }
00272 
00273 /* network_client_input();                                                                                          
00274  *                                                                                                                  
00275  * Called by libevent from event initialized in network_accept_client();                                            
00276  *                                                                                                                  
00277  * Theoretical data isolation would specify that we do the read and pass
00278  * the data to the telnet code. As an optimization, we allow telnet to 
00279  * complete the read.
00280  */
00281 static void network_client_input(int fd, short event, void *arg)
00282 {
00283         struct DESC *client = (DESC *) arg;
00284         int avail, net_length;
00285 
00286         dprintk("fd = %d, head = %d, tail = %d", client->fd, client->ringhead,
00287                         client->ringtail);
00288         if(client->ringtail < client->ringhead) {
00289                 avail = client->ringhead - client->ringtail;
00290                 handle_errno(net_length = read(client->fd,
00291                                                                            client->ringbuffer + client->ringtail,
00292                                                                            avail));
00293         } else {
00294                 if(client->ringhead == 0) {
00295                         avail = RING_LENGTH - client->ringtail;
00296                         handle_errno(net_length = read(client->fd,
00297                                                                                    client->ringbuffer +
00298                                                                                    client->ringtail, avail));
00299                         client->ringtail = (client->ringtail + net_length) % RING_LENGTH;
00300                 } else {
00301                         avail = RING_LENGTH - client->ringtail;
00302                         handle_errno(net_length = read(client->fd,
00303                                                                                    client->ringbuffer +
00304                                                                                    client->ringtail, avail));
00305                         client->ringtail = (client->ringtail + net_length) % RING_LENGTH;
00306                         if(net_length && net_length == avail) {
00307                                 handle_errno(net_length += read(client->fd,
00308                                                                                                 client->ringbuffer,
00309                                                                                                 client->ringhead - 1));
00310                                 client->ringtail += net_length - avail;
00311                         }
00312                 }
00313         }
00314 
00315         dprintk("net_length = %d", net_length);
00316 
00317         if(net_length == 0) {
00318                 printf(" client disconnected.\n");
00319                 client_disconnect(client);
00320                 return;
00321         }
00322 
00323         telnet_read_ring(client);
00324 }
00325 
00326 void network_write(DESC * client, char *buffer, int length)
00327 {
00328         bufferevent_write(d->sock_buff, buffer, length);
00329         client->output_tot += length;
00330 }

Generated on Mon May 28 04:25:25 2007 for BattletechMUX by  doxygen 1.4.7