int socks5_negotiate(int clisock, struct conndesc *conn) { u_int i; char hostname[256]; u_char nmethods, len, junk; struct sockaddr_in rem_in; struct socks5_req req5; struct socks5_v_repl rep5; struct hostent *hent; req5.vn = 5; req5.rsv = 0; /* * Start by retrieving number of methods, version number has * already been consumed by the calling procedure */ if (atomicio(read, clisock, &nmethods, 1) != 1) { warnv(1, "read()"); return (-1); } /* Eat up methods */ i = 0; while (i++ < nmethods) if (atomicio(read, clisock, &junk, 1) != 1) { warnv(1, "read()"); return (-1); } /* * We don't support any authentication methods yet, so simply * ignore it and send reply with no authentication required. */ rep5.ver = 5; rep5.res = 0; if (atomicio(write, clisock, &rep5, 2) != 2) { warnv(1, "write()"); return (-1); } /* Receive data up to atyp */ if (atomicio(read, clisock, &req5, 4) != 4) { warnv(1, "read()"); return (-1); } if (req5.vn != 5) return (-1); memset(&rem_in, 0, sizeof(rem_in)); switch (req5.atyp) { case SOCKS5_ATYP_IPV4: if (atomicio(read, clisock, &req5.destaddr, 4) != 4) { warnv(1, "read()"); return (-1); } rem_in.sin_family = AF_INET; rem_in.sin_addr.s_addr = req5.destaddr; break; case SOCKS5_ATYP_FQDN: if (atomicio(read, clisock, &len, 1) != 1) { warnv(1, "read()"); return (-1); } if (atomicio(read, clisock, hostname, len) != len) { warnv(1, "read()"); return (-1); } hostname[len] = '\0'; if ((hent = gethostbyname(hostname)) == NULL) { /* XXX no hstrerror() on solaris */ #ifndef __sun__ warnxv(1, "gethostbyname(): %s", hstrerror(h_errno)); #endif /* __sun__ */ return (-1); } rem_in.sin_family = AF_INET; rem_in.sin_addr = *(struct in_addr *)hent->h_addr; break; default: return (-1); } if (atomicio(read, clisock, &req5.destport, 2) != 2) { warnv(1, "read()"); return (-1); } rem_in.sin_port = req5.destport; /* * Now we have a filled in in_addr for the target host: * target_in, no socket yet. This is provided by the command * specific functions multiplexed in the next switch * statement. */ switch (req5.cd) { case SOCKS5_CD_CONNECT: return (socks5_connect(clisock, &rem_in, &req5, conn)); case SOCKS5_CD_BIND: signal_setup(); event_dispatch(); return (socks5_bind(clisock, &rem_in, &req5)); case SOCKS5_CD_UDP_ASSOC: default: return (-1); } }
int socks4_negotiate(int clisock, struct conndesc *conn) { u_char data, *addr; char hostname[256]; int ret; struct socks4_hdr hdr4; struct sockaddr_in rem_in; struct hostent *hent; /* This is already implied ... */ hdr4.vn = SOCKS4_VN; ret = -1; /* Get the seven first bytes after version, until USERID */ if (atomicio(read, clisock, &hdr4.cd, 7) != 7) return (-1); switch (hdr4.cd) { case SOCKS4_CD_CONNECT: case SOCKS4_CD_RESOLVE: /* We can only do connect & resolve. */ break; default: warnxv(0, "Client attempted unsupported SOCKS4 command %d", hdr4.cd); return (-1); }; /* Eat the username; it is not used */ while ((ret = atomicio(read, clisock, &data, 1)) == 1 && data != 0); if (ret != 1) return (-1); memset(&rem_in, 0, sizeof(rem_in)); rem_in.sin_family = AF_INET; rem_in.sin_port = hdr4.destport; addr = (unsigned char *)&hdr4.destaddr; /* SOCKS4A or tor-resolve? */ if ((addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] != 0) || (hdr4.cd == SOCKS4_CD_RESOLVE && hdr4.destport == 0)) { if (_getstr(clisock, hostname, sizeof(hostname)) < 0) return (-1); if ((hent = gethostbyname(hostname)) == NULL) { hdr4.cd = SOCKS4_REP_REJECT; } else { rem_in.sin_addr = *(struct in_addr *)hent->h_addr; /* * Send back the resolved address as well, for * tor-resolve. */ hdr4.destaddr = rem_in.sin_addr.s_addr; } } else { rem_in.sin_addr.s_addr = hdr4.destaddr; } return (_socks4_tryconnect(clisock, &rem_in, &hdr4, conn)); }