/* * netopen.c * * Open a TCP network connection to the specified hostname and port. * This might need some porting work to work on Win32. */ #include "network.h" #include #ifdef WIN32 #include #include #include #include /* This is basically a lame subset of stdio functionality */ #define NETCON_BUF 32768 struct netcon { int is_output; int buffill; char *bufdata; SOCKET sock; unsigned char buffer[NETCON_BUF]; }; static int winsock_started = 0; int socket_errno; /* For debuggability */ static void end_winsock(void) { if ( winsock_started ) { WSACleanup(); winsock_started = 0; } } static void start_winsock(void) { WSADATA wd; if ( !winsock_started && !WSAStartup(MAKEWORD(2,0), &wd) ) { winsock_started = 1; atexit(end_winsock); } } static int input_get(netcon_t nc) { int rv; rv = recv(nc->sock, nc->buffer, NETCON_BUF, 0); if ( rv == SOCKET_ERROR ) socket_errno = WSAGetLastError(); nc->bufdata = nc->buffer; nc->buffill = (rv == SOCKET_ERROR) ? 0 : rv; return rv; } static int output_send(netcon_t nc) { char *p = nc->buffer; int count = nc->buffill; int oc = count; int rv; while ( count ) { rv = send(nc->sock, p, count, 0); if ( rv == 0 || rv == SOCKET_ERROR ) { socket_errno = WSAGetLastError(); return -1; } p += rv; count -= rv; } nc->bufdata = nc->buffer; nc->buffill = 0; return oc; } int fgetc_network(netcon_t nc) { int rv; if ( !nc->buffill ) { rv = input_get(nc); if ( rv == 0 || rv == SOCKET_ERROR ) return EOF; } rv = *nc->bufdata; nc->bufdata++; nc->buffill--; return rv; } char *fgets_network(char *s, int size, netcon_t nc) { int ch; char *p = s; int cr = 0; if ( size == 0 ) return NULL; while ( size > 1 ) { ch = fgetc_network(nc); if ( ch == EOF ) break; *p++ = ch; cr++; if ( ch == '\n' ) break; } *p = '\0'; return cr ? s : NULL; } int fputs_network(const char *s, netcon_t nc) { int b = strlen(s); int sl; while ( b ) { sl = NETCON_BUF - nc->buffill; /* Space left */ if ( !sl ) { if ( output_send(nc) <= 0 ) return EOF; /* Error */ else continue; } if ( sl > b ) sl = b; memcpy(nc->bufdata, s, sl); nc->buffill += sl; nc->bufdata += sl; s += sl; b -= sl; } return 0; } netcon_t fopen_network(const char *name, int port, int is_write) { struct hostent *he; SOCKET sock; struct in_addr **haddr; struct sockaddr_in si; struct netcon *nc = malloc(sizeof(struct netcon)); if ( !nc ) return NULL; /* FIX: We should use getaddrinfo() instead */ he = gethostbyname(name); if ( !he || he->h_addrtype != AF_INET ) goto bail1; sock = socket(PF_INET, SOCK_STREAM, 0); if ( sock == INVALID_SOCKET ) goto bail1; for ( haddr = (struct in_addr **)he->h_addr_list ; *haddr ; haddr++ ) { memset(&si, 0, sizeof si); si.sin_family = AF_INET; si.sin_port = htons(port); memcpy(&si.sin_addr, *haddr, sizeof si.sin_addr); if ( !connect(sock, (struct sockaddr *)&si, sizeof si) ) break; } if ( !*haddr ) goto bail2; /* Construct a struct netcon object */ nc->bufdata = nc->buffer; nc->buffill = 0; nc->sock = sock; nc->is_output = is_write; return nc; bail2: closesocket(sock); bail1: free(nc); return NULL; } int fclose_network(netcon_t nc) { int rv = 0; if ( nc->is_output && nc->buffill ) { if ( output_send(nc) <= 0 ) rv = EOF; } closesocket(nc->sock); free(nc); return rv; } #else /* Unix, or something else which implements BSD sockets correctly */ #include #include #include #include #include #include /* Winsuck-compatibility */ typedef int SOCKET; #define INVALID_SOCKET (-1) #define start_winsock() do { } while (0) static netcon_t sock_to_netcon(int sock, int is_write) { FILE *f = fdopen(sock, is_write ? "w" : "r"); if ( !f ) { close(sock); return NULL; } return f; } #endif #ifdef AF_INET6 static SOCKET open_socket(const char *name, int port) { SOCKET sock; struct addrinfo *ai, *ap; struct addrinfo hints; char port_str[32]; start_winsock(); memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; sprintf(port_str, "%d", port); if (getaddrinfo(name, port_str, &hints, &ai)) return INVALID_SOCKET; sock = INVALID_SOCKET; for (ap = ai; ap; ap = ap->ai_next) { sock = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); if ( sock == INVALID_SOCKET ) continue; if ( !connect(sock, ap->ai_addr, ap->ai_addrlen) ) break; close(sock); sock = INVALID_SOCKET; } freeaddrinfo(ai); return sock; } #else /* Legacy IPv4-only API */ static SOCKET open_socket(const char *name, int port) { struct hostent *he; SOCKET sock; struct in_addr **haddr; struct sockaddr_in si; start_winsock(); he = gethostbyname(name); if ( !he || he->h_addrtype != AF_INET ) return INVALID_SOCKET; sock = socket(PF_INET, SOCK_STREAM, 0); if ( sock == INVALID_SOCKET ) return INVALID_SOCKET; for ( haddr = (struct in_addr **)he->h_addr_list ; *haddr ; haddr++ ) { memset(&si, 0, sizeof si); si.sin_family = AF_INET; si.sin_port = htons(port); memcpy(&si.sin_addr, *haddr, sizeof si.sin_addr); if ( !connect(sock, (struct sockaddr *)&si, sizeof si) ) break; } if ( !*haddr ) { close(sock); return INVALID_SOCKET; } return sock; } #endif netcon_t fopen_network(const char *name, int port, int is_write) { SOCKET sock; sock = open_socket(name, port); if (sock == INVALID_SOCKET) return NULL; return sock_to_netcon(sock, is_write); }