diff --git a/common.h b/common.h index 31d5d39..b808183 100644 --- a/common.h +++ b/common.h @@ -65,6 +65,7 @@ extern WSADATA windows_sucks; #include #include #include +#include #include #include diff --git a/lua.c b/lua.c index 1943e70..dde4175 100644 --- a/lua.c +++ b/lua.c @@ -187,6 +187,18 @@ int icelua_initfetch(void) ? 0 : main_argc - main_largstart); + if(to_client_local.sockfd == -1) + to_client_local.sockfd = SOCKFD_LOCAL; + + lua_getglobal(lstate_client, "common"); + lua_getglobal(lstate_client, "client"); + lua_pushstring(lstate_client, mod_basedir+4); + lua_setfield(lstate_client, -2, "base_dir"); + lua_pop(lstate_client, 1); + lua_pushstring(lstate_client, mod_basedir+4); + lua_setfield(lstate_client, -2, "base_dir"); + lua_pop(lstate_client, 1); + snprintf(xpath, 128, "%s/main_client.lua", mod_basedir); lua_pushcfunction(lstate_client, icelua_fn_common_fetch_block); lua_pushstring(lstate_client, "lua"); @@ -261,18 +273,6 @@ int icelua_init(void) icelua_loadbasefuncs(lstate_server); // shove some pathnames in - if(lstate_client != NULL && mod_basedir != NULL) - { - lua_getglobal(lstate_client, "common"); - lua_getglobal(lstate_client, "client"); - lua_pushstring(lstate_client, mod_basedir+4); - lua_setfield(lstate_client, -2, "base_dir"); - lua_pop(lstate_client, 1); - lua_pushstring(lstate_client, mod_basedir+4); - lua_setfield(lstate_client, -2, "base_dir"); - lua_pop(lstate_client, 1); - } - if(lstate_server != NULL) { lua_getglobal(lstate_server, "common"); diff --git a/lua_fetch.h b/lua_fetch.h index 15196d0..8bd9bbf 100644 --- a/lua_fetch.h +++ b/lua_fetch.h @@ -140,7 +140,8 @@ int icelua_fn_common_fetch_start(lua_State *L) cfetch_ftype = strdup(ftype); cfetch_fname = strdup(fname); - net_packet_push(blen, buf, -1, &(to_client_local.send_head), &(to_client_local.send_tail)); + net_packet_push(blen, buf, to_client_local.sockfd + , &(to_client_local.send_head), &(to_client_local.send_tail)); lua_pushboolean(L, 1); return 1; diff --git a/network.c b/network.c index 5541772..68e75ec 100644 --- a/network.c +++ b/network.c @@ -29,6 +29,80 @@ client_t to_client_local; client_t to_clients[CLIENT_MAX]; // TODO: binary search tree +// SNIP: http://www.eyrie.org/~eagle/software/rra-c-util/ +/* + * Replacement for a missing inet_ntop. + * + * Provides an implementation of inet_ntop that only supports IPv4 addresses + * for hosts that are missing it. If you want IPv6 support, you need to have + * a real inet_ntop function; this function is only provided so that code can + * call inet_ntop unconditionally without needing to worry about whether the + * host supports IPv6. + * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * + * Written by Russ Allbery + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. + */ + +// Modified by GreaseMonkey, to support IPv6 and also to glue in nicely. +// This is also to be treated as public domain. +// ANYHOW, let's go. + +// This may already be defined by the system headers. +#ifndef INET_ADDRSTRLEN +# define INET_ADDRSTRLEN 16 +#endif + +// Systems old enough to not support inet_ntop may not have this either. +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT EDOM +#endif + +#if WIN32 +const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) +{ + const uint8_t *p; + + if (af == AF_INET) + { + if (cnt < INET_ADDRSTRLEN) + return NULL; + + p = src; + snprintf(dst, cnt, "%u.%u.%u.%u", + (unsigned int) (p[0] & 0xff), (unsigned int) (p[1] & 0xff), + (unsigned int) (p[2] & 0xff), (unsigned int) (p[3] & 0xff)); + } else if (af == AF_INET6) { + if (cnt < 5*8) + return NULL; + + p = src; + snprintf(dst, cnt, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + (unsigned int) (p[0] & 0xff), (unsigned int) (p[1] & 0xff), + (unsigned int) (p[2] & 0xff), (unsigned int) (p[3] & 0xff), + (unsigned int) (p[4] & 0xff), (unsigned int) (p[5] & 0xff), + (unsigned int) (p[6] & 0xff), (unsigned int) (p[7] & 0xff), + (unsigned int) (p[8] & 0xff), (unsigned int) (p[9] & 0xff), + (unsigned int) (p[10] & 0xff), (unsigned int) (p[11] & 0xff), + (unsigned int) (p[12] & 0xff), (unsigned int) (p[13] & 0xff), + (unsigned int) (p[14] & 0xff), (unsigned int) (p[15] & 0xff)); + } else { + return NULL; + } + return dst; +} +#endif +// END SNIP + int net_packet_push(int len, const char *data, int sockfd, packet_t **head, packet_t **tail) { if(len > PACKET_LEN_MAX) @@ -150,6 +224,50 @@ void net_packet_free(packet_t *pkt, packet_t **head, packet_t **tail) free(pkt); } +void net_deinit_client(client_t *cli) +{ + while(cli->head != NULL) + net_packet_free(cli->head, &(cli->head), &(cli->tail)); + while(cli->send_head != NULL) + net_packet_free(cli->send_head, &(cli->send_head), &(cli->send_tail)); + + cli->sockfd = -1; +} + +void net_kick_sockfd_immediate(int sockfd, char *msg) +{ + char buf[260]; + buf[0] = 0x17; + buf[1] = strlen(msg); + memcpy(buf+2, msg, (int)(uint8_t)buf[1]); + buf[2+(int)(uint8_t)buf[1]] = 0x00; + + fprintf(stderr, "KICK: %i \"%s\"\n", sockfd, msg); + // only send what's necessary + send(sockfd, buf, ((int)(uint8_t)buf[1])+1, 0); + + // nuke it + close(sockfd); +} + +void net_kick_client_immediate(client_t *cli, char *msg) +{ + if(cli == &to_client_local) + { + fprintf(stderr, "KICK: local \"%s\"\n", msg); + fprintf(stderr, "PANIC: I don't know how to handle a local client kick yet!\n"); + fflush(stderr); + abort(); + } + + if(cli == NULL) + return; + + net_kick_sockfd_immediate(cli->sockfd, msg); + cli->sockfd = -1; + net_deinit_client(cli); +} + const char *net_aux_gettype_str(int ftype) { switch(ftype) @@ -378,12 +496,15 @@ void net_eat_s2c(client_t *cli) { fprintf(stderr, "ERROR: string not zero-terminated!\n"); } else if(mod_basedir == NULL) { - mod_basedir = strdup(1+pkt->data); + mod_basedir = strdup(2+pkt->data); boot_mode |= 8; + printf("base dir = \"%s\"\n", mod_basedir); } else { fprintf(stderr, "ERROR: base dir already defined!\n"); // TODO: make this fatal } + + net_packet_free(pkt, &(cli->head), &(cli->tail)); } break; case 0x31: { // 0x31 clen.u32 ulen.u32: @@ -566,7 +687,6 @@ void net_flush_parse_c2s(client_t *cli) void net_flush_parse_s2c(client_t *cli) { - // TODO! int offs = 0; int len; char *data; @@ -602,6 +722,45 @@ void net_flush_parse_s2c(client_t *cli) } } +client_t *net_find_sockfd(int sockfd) +{ + int i; + client_t *cli; + + if(sockfd == SOCKFD_LOCAL) + { + return &to_client_local; + } else if(sockfd >= 0) { + for(i = 0; i < CLIENT_MAX; i++) + if(to_clients[i].sockfd == sockfd) + return &to_clients[i]; + } else { + return NULL; + } + + return NULL; +} + +client_t *net_alloc_sockfd(int sockfd) +{ + int i; + client_t *cli = net_find_sockfd(sockfd); + + if(cli != NULL) + return cli; + + for(i = 0; i < CLIENT_MAX; i++) + { + if(to_clients[i].sockfd == -1) + { + to_clients[i].sockfd = sockfd; + return &to_clients[i]; + } + } + + return NULL; +} + int net_flush_transfer(client_t *cfrom, client_t *cto, packet_t *pfrom) { if(cto->isfull) @@ -648,7 +807,6 @@ int net_flush_transfer(client_t *cfrom, client_t *cto, packet_t *pfrom) */ // and of course the serialised version: - // TODO: make this work over a network! if(cto == &to_server) { memcpy(cfrom->spkt_buf + cfrom->spkt_len, pfrom->data, pfrom->len); @@ -663,7 +821,67 @@ int net_flush_transfer(client_t *cfrom, client_t *cto, packet_t *pfrom) void net_flush_accept_one(int sockfd, struct sockaddr_storage *ss, socklen_t slen) { + char xstr[128]; + xstr[0] = '?'; + xstr[1] = '\0'; + int cport = 0; + switch(ss->ss_family) + { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in *)(ss))->sin_addr) + , xstr, 127); + cport = ((struct sockaddr_in *)(ss))->sin_port; + break; + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)(ss))->sin6_addr) + , xstr, 127); + cport = ((struct sockaddr_in6 *)(ss))->sin6_port; + break; + } + printf("connection from %s, port %i, family %i\n", xstr, cport, ss->ss_family); + + // disable Nagle's algo + int yes = 1; + if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&yes, sizeof(yes)) == -1) + { + net_kick_sockfd_immediate(sockfd, "Could not disable Nagle's algorithm!" + " Kicked because gameplay will be complete shit otherwise."); + return; + } + + // set connection nonblocking + yes = 1; +#ifdef WIN32 + if(ioctlsocket(sockfd,FIONBIO,(void *)&yes) == -1) { +#else + if(fcntl(sockfd, F_SETFL, + fcntl(sockfd, F_GETFL) | O_NONBLOCK) == -1) { +#endif + net_kick_sockfd_immediate(sockfd, "Could not set up a nonblocking connection!"); + return; + } + + // get a slot + client_t *cli = net_alloc_sockfd(sockfd); + + if(cli == NULL) + { + net_kick_sockfd_immediate(sockfd, "Server ran out of free slots!"); + return; + } + + // send pkg basedir packet + { + char buf[260]; + buf[0] = 0x0F; + buf[1] = strlen(mod_basedir); + memcpy(buf+2, mod_basedir, (int)(uint8_t)buf[1]); + buf[2+(int)(uint8_t)buf[1]] = 0x00; + + net_packet_push(2+((int)(uint8_t)buf[1])+1, buf, sockfd, + &(to_server.send_head), &(to_server.send_tail)); + } } void net_flush_accept(void) @@ -676,10 +894,11 @@ void net_flush_accept(void) if(sockfd == -1) { - int err = errno; #ifdef WIN32 + int err = WSAGetLastError(); if(err != WSAEWOULDBLOCK) #else + int err = errno; if(err != EAGAIN && err != EWOULDBLOCK) #endif { @@ -698,10 +917,11 @@ void net_flush_accept(void) if(sockfd == -1) { - int err = errno; #ifdef WIN32 + int err = WSAGetLastError(); if(err != WSAEWOULDBLOCK) #else + int err = errno; if(err != EAGAIN && err != EWOULDBLOCK) #endif { @@ -713,6 +933,125 @@ void net_flush_accept(void) } } +void net_flush_snr(client_t *cli) +{ + if(cli == &to_client_local) + { + if(boot_mode & 2) + { + // don't do anything, it's already in the buffer + return; + } else { + { + int bs = send(cli->sockfd, cli->spkt_buf, + cli->spkt_len, 0); + + if(bs == -1) + { +#ifdef WIN32 + int err = WSAGetLastError(); + if(err != WSAEWOULDBLOCK) +#else + int err = errno; + if(err != EAGAIN && err != EWOULDBLOCK) +#endif + { + perror("net_flush_snr(client.send)"); + net_kick_client_immediate(cli, "Error sending packet!"); + return; + } + } else if(bs > 0) { + //printf("sent data! %i\n", bs); + cli->spkt_len -= bs; + memmove(cli->spkt_buf, cli->spkt_buf+bs, cli->spkt_len); + } + } + + { + int bs = recv(cli->sockfd, cli->rpkt_buf+cli->rpkt_len, + PACKET_LEN_MAX*2-cli->rpkt_len, 0); + + if(bs == -1) + { +#ifdef WIN32 + int err = WSAGetLastError(); + if(err != WSAEWOULDBLOCK) +#else + int err = errno; + if(err != EAGAIN && err != EWOULDBLOCK) +#endif + { + perror("net_flush_snr(client.recv)"); + net_kick_client_immediate(cli, "Error receiving packet!"); + return; + } + } else if(bs == 0) { + fprintf(stderr, "%i: recv: connection axed\n", cli->sockfd); + net_kick_client_immediate(cli, "Connection axed."); + return; + } else { + //printf("got data! %i\n", bs); + cli->rpkt_len += bs; + } + } + } + } else { + //printf("send sockfd %i %i %i\n", cli->sockfd, cli->rpkt_len, cli->rpkt_len); + { + int bs = send(cli->sockfd, cli->rpkt_buf, + cli->rpkt_len, 0); + + if(bs == -1) + { +#ifdef WIN32 + int err = WSAGetLastError(); + if(err != WSAEWOULDBLOCK) +#else + int err = errno; + if(err != EAGAIN && err != EWOULDBLOCK) +#endif + { + perror("net_flush_snr(server.send)"); + net_kick_client_immediate(cli, "Error sending packet!"); + return; + } + } else if(bs > 0) { + //printf("server sent data! %i\n", bs); + cli->rpkt_len -= bs; + memmove(cli->rpkt_buf, cli->rpkt_buf+bs, cli->rpkt_len); + } + } + + { + int bs = recv(cli->sockfd, cli->spkt_buf+cli->spkt_len, + PACKET_LEN_MAX*2-cli->spkt_len, 0); + + if(bs == -1) + { +#ifdef WIN32 + int err = WSAGetLastError(); + if(err != WSAEWOULDBLOCK) +#else + int err = errno; + if(err != EAGAIN && err != EWOULDBLOCK) +#endif + { + perror("net_flush_snr(server.recv)"); + net_kick_client_immediate(cli, "Error receiving packet!"); + return; + } + } else if(bs == 0) { + fprintf(stderr, "%i: recv: connection axed\n", cli->sockfd); + net_kick_client_immediate(cli, "Connection axed."); + return; + } else { + //printf("server got data! %i\n", bs); + cli->spkt_len += bs; + } + } + } +} + void net_flush(void) { packet_t *pkt, *npkt; @@ -720,6 +1059,8 @@ void net_flush(void) if(boot_mode & 2) { + net_flush_accept(); + // clear full flags for(i = 0; i < CLIENT_MAX; i++) to_clients[i].isfull = 0; @@ -733,14 +1074,23 @@ void net_flush(void) //printf("pkt = %016llX\n", pkt); - client_t *cli = NULL; + client_t *cli = net_find_sockfd(pkt->sockfd); - // TODO: find correct client properly - cli = &to_client_local; - - net_flush_transfer(&to_server, cli, pkt); + if(cli == NULL) + { + fprintf(stderr, "EDOOFUS: given sockfd %i could not be found!\n" + , pkt->sockfd); + fflush(stderr); + abort(); + } else { + net_flush_transfer(&to_server, cli, pkt); + } } + for(i = 0; i < CLIENT_MAX; i++) + if(to_clients[i].sockfd != -1) + net_flush_snr(&to_clients[i]); + // parse the incoming stuff for(i = 0; i < CLIENT_MAX; i++) if(to_clients[i].sockfd != -1) @@ -760,6 +1110,8 @@ void net_flush(void) net_flush_transfer(&to_client_local, &to_server, pkt); } + net_flush_snr(&to_client_local); + net_flush_parse_s2c(&to_client_local); } @@ -794,25 +1146,22 @@ int net_gethost(char *name, int port, struct sockaddr *sa, size_t alen) best = NULL; for(fol = res; fol != NULL; fol = fol->ai_next) { -#ifdef WIN32 - printf("lookup: Family %i - Not giving IP because WINDOWS SUCKS ASS\n", fol->ai_family); -#else char xstr[128]; xstr[0] = '?'; xstr[1] = '\0'; switch(fol->ai_family) { - case AF_INET: + case AF_INET: { inet_ntop(AF_INET, &(((struct sockaddr_in *)(fol->ai_addr))->sin_addr) , xstr, 127); - break; - case AF_INET6: + } break; + case AF_INET6: { inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)(fol->ai_addr))->sin6_addr) , xstr, 127); - break; + } break; } printf("lookup: %s\n", xstr); -#endif + // NOTE: prioritising IPv4 over IPv6. if(best == NULL) { @@ -847,6 +1196,18 @@ int net_connect(void) if(connect(sockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1) return error_perror("net_connect(connect)"); + int yes = 1; +#ifdef WIN32 + if(ioctlsocket(sockfd, FIONBIO, (void *)&yes)) +#else + if(fcntl(sockfd, F_SETFL, + fcntl(sockfd, F_GETFL) | O_NONBLOCK)) +#endif + return error_perror("net_connect(nonblock)"); + + if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&yes, sizeof(yes)) == -1) + return error_perror("net_connect(nodelay)"); + to_client_local.sockfd = sockfd; } break; @@ -987,14 +1348,6 @@ void net_unbind(void) } } -void net_deinit_client(client_t *cli) -{ - while(cli->head != NULL) - net_packet_free(cli->head, &(cli->head), &(cli->tail)); - while(cli->send_head != NULL) - net_packet_free(cli->send_head, &(cli->send_head), &(cli->send_tail)); -} - int net_init(void) { int i;