/* scut's leet network library ;) * 1999 (c) scut * * networking routines * based on my hbot networking sources, * revised, extended and adapted 990405 * extended, improved and fixed 990430 * * nearly all of this code wouldn't have been possible without w. richard stevens * excellent network coding book. if you are interested in network coding, * there is no way around it. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "network.h" int net_readtimeout = NET_READTIMEOUT; int net_conntimeout = NET_CONNTIMEOUT; int net_identtimeout = NET_IDENTTIMEOUT; int net_socks_connect (char *socks, unsigned short int sport, char *server, unsigned short int port, int sec) { int s5s; struct sockaddr_in cs; s5s = net_connect (&cs, socks, sport, NULL, 0, sec); if (s5s == -1) return (-1); if (net_socks_put_s5info (s5s, server, port, sec) == -1) { close (s5s); return (-1); } return (s5s); } int net_socks_put_s5info (int s5s, char *server, unsigned short int port, int sec) { int n; char buff[1024]; /* v5 + noauth */ net_write (s5s, "\x05\x01%c", 0); if (net_rtimeout (s5s, sec) == -1) return (-1); recv (s5s, buff, sizeof (buff), 0); /* chain us =) */ net_write (s5s, "\x05\x01%c\x03%c%s%c%c", 0, strlen (server), server, (port >> 8) & 0xff, port & 0xff); if (net_rtimeout (s5s, sec) == -1) return (-1); n = recv (s5s, buff, sizeof (buff), 0); if (buff[1] != 0x00) { return (-1); } return (1); } int net_parseip (char *inp, char **ip, unsigned short int *port) { int n; if (inp == NULL) return (0); if (strchr (inp, ':') == NULL) return (0); *ip = calloc (1, 256); if (*ip == NULL) return (0); n = sscanf (inp, "%[^:]:%hu", *ip, port); if (n != 2) return (0); *ip = realloc (*ip, strlen (*ip) + 1); if (*ip == NULL || (*port < 1 || *port > 65535)) return (0); return (1); } char * net_getlocalip (void) { struct sockaddr_in pf; char name[255]; memset (name, '\0', sizeof (name)); if (gethostname (name, sizeof (name) - 1) == -1) { return (NULL); } pf.sin_addr.s_addr = net_resolve (name); return (strdup (inet_ntoa (pf.sin_addr)));; } char * net_peeraddress (int socket) { char * hip; struct sockaddr_in peeraddr; size_t size = sizeof (struct sockaddr_in); if (getpeername (socket, (struct sockaddr *) &peeraddr, &size) == -1) return (NULL); net_printipa (&peeraddr.sin_addr, &hip); return (hip); } #if !defined(__FreeBSD__) char * net_peername (int socket) { struct sockaddr_in peeraddr; struct hostent he, *hep; size_t size = sizeof (struct sockaddr_in); int n, h_errno; unsigned char h_buf[8192]; if (getpeername (socket, (struct sockaddr *) &peeraddr, &size) == -1) return (NULL); /* digital unix / hp-ux freaks mod here =) */ n = gethostbyaddr_r ((char *) &peeraddr.sin_addr, sizeof (struct in_addr), AF_INET, &he, h_buf, sizeof (h_buf), &hep, &h_errno); if (hep == NULL) { char *ip_str = NULL; net_printipa (&peeraddr.sin_addr, &ip_str); return (ip_str); } return (strdup (he.h_name)); } #endif FILE * net_descriptify (int socket) { FILE *fp; fp = fdopen (socket, "r+"); return ((fp == NULL) ? (NULL) : (fp)); } /* loosely based on rfc931.c */ int net_ident (char **ident, struct sockaddr_in *locals, unsigned short int localport, struct sockaddr_in *remotes, unsigned short int remoteport) { int is; /* ident socket */ struct sockaddr_in isa; int n; char identreply[512], *cp; unsigned int rmt_port, our_port; *ident = NULL; is = net_connect (&isa, inet_ntoa (remotes->sin_addr), 113, NULL, 0, net_identtimeout); if (is == -1) return (-1); /* ident request */ net_write (is, "%u,%u\r\n", remoteport, localport); memset (identreply, '\0', sizeof (identreply)); n = net_rlinet (is, identreply, sizeof(identreply) -1, net_identtimeout); if (n == -1) { close (is); return (-1); } close (is); *ident = calloc (1, 256); #ifdef DEBUG printf("%s\n", identreply); #endif n = sscanf (identreply, "%u , %u : USERID :%*[^:]:%255s", &rmt_port, &our_port, *ident); if (n != 3) { free (*ident); *ident = NULL; return (-1); } /* check the ports 'man */ if ((rmt_port != remoteport) || (our_port != localport)) { free (*ident); *ident = NULL; return (-1); } /* strip character and save some memory */ if ((cp = strchr (*ident, '\r'))) *cp = '\0'; n = strlen (*ident); *ident = realloc (*ident, n + 1); (*ident)[n] = '\0'; #ifdef DEBUG printf("ident-return: %s\n", *ident); #endif return (1); } int net_accept (int s, struct sockaddr_in *cs, int maxsec) { int flags, n; fd_set ac_s; int len; struct timeval tval; flags = fcntl(s, F_GETFL, 0); if (flags == -1) return (-1); n = fcntl(s, F_SETFL, flags | O_NONBLOCK); if (n == -1) return (-1); FD_ZERO(&ac_s); FD_SET(s, &ac_s); tval.tv_sec = maxsec; tval.tv_usec = 0; n = select(s + 1, &ac_s, NULL, NULL, maxsec ? &tval : NULL); if (n == 0) return (0); if (FD_ISSET(s, &ac_s)) { len = sizeof(struct sockaddr_in); n = accept(s, (struct sockaddr *) cs, &len); if (n == -1) { switch (errno) { case EWOULDBLOCK: case ECONNABORTED: #ifdef EPROTO case EPROTO: #endif case EINTR: if (fcntl(s, F_SETFL, flags) == -1) return (-1); return (0); default: return (-1); } } if (fcntl(s, F_SETFL, flags) == -1) return (-1); return (n); } if (fcntl(s, F_SETFL, flags) == -1) return (-1); return (0); } int net_testvip (char *ip) { struct ifi_info *ifi, *ifc; struct in_addr ip_n; if (ip == NULL) return (1); if (strcmp(ip, "*") == 0) return (1); ip_n.s_addr = net_resolve(ip); if (!(ip_n.s_addr)) return (0); ifi = net_ifi_get (AF_INET, 1); if (ifi == NULL) return (0); for (ifc = ifi; ifc != NULL; ifc = ifc->ifi_next) { if (memcmp (&ip_n.s_addr, &ifc->ifi_saddr.s_addr, sizeof (struct in_addr)) == 0) { net_ifi_free(ifi); return (1); } } net_ifi_free(ifi); return (0); } void net_ifi_free (struct ifi_info *tf) { struct ifi_info *ifi, *ifil; ifil = NULL; for (ifi = tf; ifi != NULL; ifi = ifi->ifi_next) { if (ifil) free (ifil); if (ifi->ifi_addr) free (ifi->ifi_addr); ifil = ifi; } if (ifil) free (ifil); return; } struct ifi_info * net_ifi_get (int family, int doaliases) { struct ifi_info *ifi, *ifihead, **ifipnext; int sockfd, len, lastlen, flags, myflags; char *ptr, *buf, lastname[IFNAMSIZ], *cptr; struct ifconf ifc; struct ifreq *ifr, ifrcopy; struct sockaddr_in *sinptr; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) return (NULL); lastlen = 0; len = 100 * sizeof(struct ifreq); for (;;) { buf = malloc(len); if (buf == NULL) return (NULL); ifc.ifc_len = len; ifc.ifc_buf = buf; if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { if (errno != EINVAL || lastlen != 0) return (NULL); } else { if (ifc.ifc_len == lastlen) break; lastlen = ifc.ifc_len; } len += 10 * sizeof(struct ifreq); free (buf); } ifihead = NULL; ifipnext = &ifihead; lastname[0] = 0; for (ptr = buf; ptr < buf + ifc.ifc_len;) { ifr = (struct ifreq *) ptr; if (ifr->ifr_addr.sa_family == AF_INET) len = sizeof(struct sockaddr); ptr += sizeof(ifr->ifr_name) + len; if (ifr->ifr_addr.sa_family != family) continue; myflags = 0; if ((cptr = strchr(ifr->ifr_name, ':')) != NULL) *cptr = 0; if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { if (doaliases == 0) continue; myflags = IFI_ALIAS; } memcpy(lastname, ifr->ifr_name, IFNAMSIZ); ifrcopy = *ifr; if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) return (NULL); flags = ifrcopy.ifr_flags; if ((flags & IFF_UP) == 0) continue; ifi = calloc(1, sizeof(struct ifi_info)); if (ifi == NULL) return (NULL); *ifipnext = ifi; ifipnext = &ifi->ifi_next; ifi->ifi_flags = flags; ifi->ifi_myflags = myflags; memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); ifi->ifi_name[IFI_NAME - 1] = '\0'; #ifdef DEBUG printf("got: %s\n", ifi->ifi_name); #endif switch (ifr->ifr_addr.sa_family) { case AF_INET: sinptr = (struct sockaddr_in *) &ifr->ifr_addr; memcpy(&ifi->ifi_saddr, &sinptr->sin_addr, sizeof(struct in_addr)); if (ifi->ifi_addr == NULL) { ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_addr == NULL) return (NULL); memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); } break; default: break; } } free (buf); return (ifihead); } void net_boundfree (bound *bf) { close (bf->bs); free (bf); return; } bound * net_bind (char *ip, unsigned short int port) { bound *b; int br, gsnr, lr; int len, reusetmp; struct sockaddr_in *sap; if (port >= 65536) return (NULL); b = calloc(1, sizeof (bound)); if (b == NULL) return (NULL); b->bs = socket (AF_INET, SOCK_STREAM, 0); if (b->bs == -1) goto berror; reusetmp = 1; #ifdef SO_REUSEPORT if (setsockopt (b->bs, SOL_SOCKET, SO_REUSEPORT, &reusetmp, sizeof (reusetmp)) == -1) goto berror; #else if (setsockopt (b->bs, SOL_SOCKET, SO_REUSEADDR, &reusetmp, sizeof (reusetmp)) == -1) goto berror; #endif sap = (struct sockaddr_in *) &b->bsa; sap->sin_family = AF_INET; sap->sin_port = htons (port); /* 0 = ephemeral */ if (ip != NULL) { if (strcmp (ip, "*") == 0) { sap->sin_addr.s_addr = htonl (INADDR_ANY); } else { if (!(sap->sin_addr.s_addr = net_resolve (ip))) { goto berror; } } } else { sap->sin_addr.s_addr = htonl (INADDR_ANY); } br = bind (b->bs, (struct sockaddr *) &b->bsa, sizeof (struct sockaddr)); if (br == -1) goto berror; len = sizeof (struct sockaddr); gsnr = getsockname (b->bs, (struct sockaddr *) &b->bsa, &len); b->port = ntohs (sap->sin_port); if (gsnr == -1) goto berror; lr = listen (b->bs, 16); if (lr == -1) { goto berror; } return (b); berror: free (b); return(NULL); } unsigned long int net_resolve (char *host) { long i; struct hostent *he; i = inet_addr(host); if (i == -1) { he = gethostbyname(host); if (he == NULL) { return (0); } else { return (*(unsigned long *) he->h_addr); } } return (i); } int net_assignaddr (int sd, char *sourceip, unsigned short int sourceport) { struct sockaddr_in sourcedef; if (sourceip && strcmp (sourceip, "*") == 0) sourceip = NULL; if (sourceip == NULL && sourceport == 0) return (1); /* is the IP available on the local host ? (not really necessary) */ if (sourceip && !net_testvip (sourceip)) { return (0); } memset (&sourcedef, '\0', sizeof (struct sockaddr_in)); /* if sourceip is specified, set it */ if (sourceip) { sourcedef.sin_addr.s_addr = net_resolve (sourceip); } else { sourcedef.sin_addr.s_addr = htonl (INADDR_ANY); } if (sourceport) sourcedef.sin_port = htons (sourceport); /* now set the source on the socket by binding it */ if (bind (sd, (struct sockaddr *) &sourcedef, sizeof (struct sockaddr_in)) == -1) { return (0); } return (1); } int net_connect (struct sockaddr_in *cs, char *server, unsigned short int port, char *sourceip, unsigned short int sourceport, int sec) { int n, len, error, flags; int fd; struct timeval tv; fd_set rset, wset; struct sockaddr_in csa; if (cs == NULL) cs = &csa; /* first allocate a socket */ cs->sin_family = AF_INET; cs->sin_port = htons (port); fd = socket (cs->sin_family, SOCK_STREAM, 0); if (fd == -1) return (-1); /* check wether we should change the defaults */ if (net_assignaddr (fd, sourceip, sourceport) == 0) { close (fd); return (-1); } if (!(cs->sin_addr.s_addr = net_resolve (server))) { close (fd); return (-1); } flags = fcntl (fd, F_GETFL, 0); if (flags == -1) { close (fd); return (-1); } n = fcntl (fd, F_SETFL, flags | O_NONBLOCK); if (n == -1) { close (fd); return (-1); } error = 0; n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in)); if (n < 0) { if (errno != EINPROGRESS) { close (fd); return (-1); } } if (n == 0) goto done; FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(fd, &rset); FD_SET(fd, &wset); tv.tv_sec = sec; tv.tv_usec = 0; n = select(fd + 1, &rset, &wset, NULL, &tv); if (n == 0) { close(fd); errno = ETIMEDOUT; return (-1); } if (n == -1) return (-1); if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) { if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) { len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { errno = ETIMEDOUT; return (-1); } if (error == 0) { goto done; } else { errno = error; return (-1); } } } else return (-1); done: n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); return (fd); } int net_tline (char *buf, int bufsize) { int p; for (p = 0; p < bufsize; p++) { if (buf[p] == '\n') return (p + 1); } return (-1); } #define LINET_A 1024 int net_rlineta (int fd, char **buf, int sec) { int n; /* return value */ int bufsize = 0; *buf = NULL; do { bufsize += LINET_A; *buf = realloc (*buf, bufsize); if (*buf == NULL) return (-1); n = net_rlinet (fd, *buf + bufsize - LINET_A, LINET_A, sec); if (n == -1) goto rlinetaerr; if (n >= 0) goto rlinetastrip; } while (n == -2); rlinetastrip: *buf = realloc (*buf, strlen (*buf) + 1); return (strlen (*buf)); rlinetaerr: free (*buf); return (-1); } int net_rlinet (int fd, char *buf, int bufsize, int sec) { int n; unsigned long int rb = 0; struct timeval tv_start, tv_cur; memset(buf, '\0', bufsize); (void) gettimeofday(&tv_start, NULL); do { (void) gettimeofday(&tv_cur, NULL); if (sec > 0) { if ((((tv_cur.tv_sec * 1000000) + (tv_cur.tv_usec)) - ((tv_start.tv_sec * 1000000) + (tv_start.tv_usec))) > (sec * 1000000)) { return (-1); } } n = net_rtimeout(fd, net_readtimeout); if (n <= 0) { return (-1); } n = read(fd, buf, 1); if (n <= 0) { return (n); } rb++; if (*buf == '\n') return (rb); buf++; if (rb >= bufsize) return (-2); /* buffer full */ } while (1); } long int net_rbuf (int fd, char **dst) { long int ml = 0; long int read_bytes; int p; *dst = NULL; while ((p = net_rtimeout(fd, net_readtimeout)) == 1) { *dst = (char *) realloc(*dst, ml + NET_BSIZE); if (*dst == NULL) return (-1); ml += read_bytes = read(fd, *dst + ml, NET_BSIZE); if (read_bytes == 0) { *dst = (char *) realloc(*dst, ml); if ((*dst == NULL) && (ml == 0)) { return (1); } else if (*dst == NULL) { return (-1); } else { return (ml); } } } return (-1); } int net_rbuft (int fd, char *dst, unsigned long int dsize) { unsigned long int bl = 0, m; int p; while (bl < dsize) { p = net_rtimeout(fd, net_readtimeout); if ((p == 0) || (p == -1)) { return (-1); } m = read(fd, dst + bl, (dsize - bl)); if ((m == 0) || (m == -1)) { return (-1); } bl += m; } return (1); } int net_rtimeout (int fd, int sec) { fd_set rset; struct timeval tv; int n, error, flags; error = 0; flags = fcntl(fd, F_GETFL, 0); n = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (n == -1) return (-1); FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; /* now we wait until more data is received then the tcp low level watermark, * which should be setted to 1 in this case (1 is default) */ n = select(fd + 1, &rset, NULL, NULL, &tv); if (n == 0) { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); errno = ETIMEDOUT; return (-1); } if (n == -1) { return (-1); } /* socket readable ? */ if (FD_ISSET(fd, &rset)) { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); return (1); } else { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); errno = ETIMEDOUT; return (-1); } } void net_write (int fd, const char *str, ...) { char tmp[1025]; va_list vl; int i; va_start(vl, str); memset(tmp, 0, sizeof(tmp)); i = vsnprintf(tmp, sizeof(tmp), str, vl); va_end(vl); #ifdef DEBUG printf("[snd] %s\n", tmp); #endif send(fd, tmp, i, 0); return; } int net_printip (struct in_addr *ia, char *str, size_t len) { unsigned char *ipp; ipp = (unsigned char *) &ia->s_addr; snprintf (str, len - 1, "%d.%d.%d.%d", ipp[0], ipp[1], ipp[2], ipp[3]); return (0); } int net_printipa (struct in_addr *ia, char **str) { unsigned char *ipp; ipp = (unsigned char *) &ia->s_addr; *str = calloc (1, 256); if (*str == NULL) return (1); snprintf (*str, 255, "%d.%d.%d.%d", ipp[0], ipp[1], ipp[2], ipp[3]); *str = realloc (*str, strlen (*str) + 1); return ((*str == NULL) ? 1 : 0); }