diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/assuan-socket.c | 177 | ||||
-rw-r--r-- | src/assuan.h.in | 12 | ||||
-rw-r--r-- | src/libassuan.def | 1 | ||||
-rw-r--r-- | src/libassuan.vers | 1 |
4 files changed, 176 insertions, 15 deletions
diff --git a/src/assuan-socket.c b/src/assuan-socket.c index 9a6ee66..12e9e38 100644 --- a/src/assuan-socket.c +++ b/src/assuan-socket.c @@ -657,9 +657,11 @@ do_writen (assuan_context_t ctx, assuan_fd_t sockfd, } -/* Connect using the SOCKS5 protocol. */ +/* Connect using the SOCKS5 protocol. */ static int -socks5_connect (assuan_context_t ctx, int sock, +socks5_connect (assuan_context_t ctx, int sock, unsigned short socksport, + const char *credentials, + const char *hostname, unsigned short hostport, struct sockaddr *addr, socklen_t length) { int ret; @@ -669,16 +671,36 @@ socks5_connect (assuan_context_t ctx, int sock, size_t proxyaddrlen; struct sockaddr_in6 *addr_in6; struct sockaddr_in *addr_in; - unsigned char buffer[22]; - size_t buflen; + unsigned char buffer[22+512]; /* The extra 512 gives enough space + for username/password or the + hostname. */ + size_t buflen, hostnamelen; + int method; /* memset (&proxyaddr_in6, 0, sizeof proxyaddr_in6); */ memset (&proxyaddr_in, 0, sizeof proxyaddr_in); + /* Either HOSTNAME or ADDR may be given. */ + if (hostname && addr) + { + gpg_err_set_errno (EINVAL); + return -1; + } + + /* If a hostname is given it must fit into our buffer and it must be + less than 256 so that its length can be encoded in one byte. */ + hostnamelen = hostname? strlen (hostname) : 0; + if (hostnamelen > 255) + { + gpg_err_set_errno (ENAMETOOLONG); + return -1; + } + /* Connect to local host. */ - /* Fixme: First try to use IPv6. */ + /* Fixme: First try to use IPv6 but note that + _assuan_sock_connect_byname created the socket with AF_INET. */ proxyaddr_in.sin_family = AF_INET; - proxyaddr_in.sin_port = htons (tor_mode); + proxyaddr_in.sin_port = htons (socksport); proxyaddr_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); proxyaddr = (struct sockaddr *)&proxyaddr_in; proxyaddrlen = sizeof proxyaddr_in; @@ -687,7 +709,11 @@ socks5_connect (assuan_context_t ctx, int sock, return ret; buffer[0] = 5; /* RFC-1928 VER field. */ buffer[1] = 1; /* NMETHODS */ - buffer[2] = 0; /* Method: No authentication required. */ + if (credentials) + method = 2; /* Method: username/password authentication. */ + else + method = 0; /* Method: No authentication required. */ + buffer[2] = method; /* Negotiate method. */ ret = do_writen (ctx, sock, buffer, 3); @@ -696,7 +722,7 @@ socks5_connect (assuan_context_t ctx, int sock, ret = do_readn (ctx, sock, buffer, 2); if (ret) return ret; - if (buffer[0] != 5 || buffer[1] != 0 ) + if (buffer[0] != 5 || buffer[1] != method ) { /* Socks server returned wrong version or does not support our requested method. */ @@ -704,11 +730,70 @@ socks5_connect (assuan_context_t ctx, int sock, return -1; } + if (credentials) + { + const char *password; + int ulen, plen; + + password = strchr (credentials, ':'); + if (!password) + { + gpg_err_set_errno (EINVAL); /* No password given. */ + return -1; + } + ulen = password - credentials; + password++; + plen = strlen (password); + if (!ulen || ulen > 255 || !plen || plen > 255) + { + gpg_err_set_errno (EINVAL); + return -1; + } + + buffer[0] = 1; /* VER of the sub-negotiation. */ + buffer[1] = ulen; + buflen = 2; + memcpy (buffer+buflen, credentials, ulen); + buflen += ulen; + buffer[buflen++] = plen; + memcpy (buffer+buflen, password, plen); + buflen += plen; + ret = do_writen (ctx, sock, buffer, buflen); + wipememory (buffer, buflen); + if (ret) + return ret; + ret = do_readn (ctx, sock, buffer, 2); + if (ret) + return ret; + if (buffer[0] != 1) + { + /* SOCKS server returned wrong version. */ + gpg_err_set_errno (EPROTO); + return -1; + } + if (buffer[1]) + { + /* SOCKS server denied access. */ + gpg_err_set_errno (EACCES); + return -1; + } + } + /* Send request details (rfc-1928, 4). */ buffer[0] = 5; /* VER */ buffer[1] = 1; /* CMD = CONNECT */ buffer[2] = 0; /* RSV */ - if (addr->sa_family == AF_INET6) + if (hostname) + { + buffer[3] = 3; /* ATYP = DOMAINNAME */ + buflen = 4; + buffer[buflen++] = hostnamelen; + memcpy (buffer+buflen, hostname, hostnamelen); + buflen += hostnamelen; + buffer[buflen++] = (hostport >> 8); /* DST.PORT */ + buffer[buflen++] = hostport; + } + else if (addr->sa_family == AF_INET6) { addr_in6 = (struct sockaddr_in6 *)addr; @@ -729,7 +814,7 @@ socks5_connect (assuan_context_t ctx, int sock, ret = do_writen (ctx, sock, buffer, buflen); if (ret) return ret; - ret = do_readn (ctx, sock, buffer, buflen); + ret = do_readn (ctx, sock, buffer, 10 /* Length for IPv4 */); if (ret) return ret; if (buffer[0] != 5 || buffer[2] != 0 ) @@ -743,10 +828,10 @@ socks5_connect (assuan_context_t ctx, int sock, { switch (buffer[1]) { - case 0x01: /* general SOCKS server failure. */ + case 0x01: /* General SOCKS server failure. */ gpg_err_set_errno (ENETDOWN); break; - case 0x02: /* connection not allowed by ruleset. */ + case 0x02: /* Connection not allowed by ruleset. */ gpg_err_set_errno (EACCES); break; case 0x03: /* Network unreachable */ @@ -770,6 +855,15 @@ socks5_connect (assuan_context_t ctx, int sock, } return -1; } + if (buffer[3] == 4) + { + /* ATYP indicates a v6 address. We need to read the remaining + 12 bytes. */ + ret = do_readn (ctx, sock, buffer+10, 12); + if (ret) + return ret; + } + /* FIXME: We have not way to store the actual address used by the server. */ @@ -779,7 +873,7 @@ socks5_connect (assuan_context_t ctx, int sock, /* Return true if SOCKS shall be used. This is the case if tor_mode - is enabled and and the desired address is not the loopback + is enabled and the desired address is not the loopback address. */ static int use_socks (struct sockaddr *addr) @@ -874,7 +968,8 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd, } else if (use_socks (addr)) { - return socks5_connect (ctx, HANDLE2SOCKET (sockfd), addr, addrlen); + return socks5_connect (ctx, HANDLE2SOCKET (sockfd), tor_mode, + NULL, NULL, 0, addr, addrlen); } else { @@ -916,7 +1011,8 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd, if (use_socks (addr)) { - return socks5_connect (ctx, sockfd, addr, addrlen); + return socks5_connect (ctx, sockfd, tor_mode, + NULL, NULL, 0, addr, addrlen); } else { @@ -926,6 +1022,48 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd, } +/* Connect to HOST specified as host name on PORT. The current + implementation requires that either the flags ASSUAN_SOCK_SOCKS or + ASSUAN_SOCK_TOR are give in FLAGS. On success a new socket is + returned; on error ASSUAN_INVALID_FD is returned and ERRNO set. If + CREDENTIALS is not NULL, it is a string used for password based + authentication. Username and password are separated by a + colon. RESERVED must be 0. */ +assuan_fd_t +_assuan_sock_connect_byname (assuan_context_t ctx, const char *host, + unsigned short port, int reserved, + const char *credentials, unsigned int flags) +{ + int fd; + unsigned short socksport; + + if ((flags & ASSUAN_SOCK_TOR)) + socksport = TOR_PORT; + else if ((flags & ASSUAN_SOCK_SOCKS)) + socksport = SOCKS_PORT; + else + { + gpg_err_set_errno (ENOTSUP); + return ASSUAN_INVALID_FD; + } + + fd = _assuan_sock_new (ctx, AF_INET, SOCK_STREAM, 0); + if (fd == ASSUAN_INVALID_FD) + return fd; + + if (socks5_connect (ctx, fd, socksport, + credentials, host, port, NULL, 0)) + { + int save_errno = errno; + assuan_sock_close (fd); + gpg_err_set_errno (save_errno); + return -1; + } + + return fd; +} + + int _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) @@ -1257,6 +1395,15 @@ assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) return _assuan_sock_connect (sock_ctx, sockfd, addr, addrlen); } +assuan_fd_t +assuan_sock_connect_byname (const char *host, unsigned short port, + int reserved, const char *credentials, + unsigned int flags) +{ + return _assuan_sock_connect_byname (sock_ctx, + host, port, reserved, credentials, flags); +} + int assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { diff --git a/src/assuan.h.in b/src/assuan.h.in index b26fa3b..67a1c20 100644 --- a/src/assuan.h.in +++ b/src/assuan.h.in @@ -461,6 +461,14 @@ gpg_error_t assuan_set_error (assuan_context_t ctx, gpg_error_t err, /*-- assuan-socket.c --*/ +/* This flag is used with assuan_sock_connect_byname to + connect via SOCKS. */ +#define ASSUAN_SOCK_SOCKS 1 +/* This flag is used with assuan_sock_connect_byname to force a + connection via Tor even if the socket subsystem has not been + swicthed into Tor mode. This flags overrides ASSUAN_SOCK_SOCKS. */ +#define ASSUAN_SOCK_TOR 2 + /* These are socket wrapper functions to support an emulation of Unix domain sockets on Windows W32. */ gpg_error_t assuan_sock_init (void); @@ -471,6 +479,10 @@ int assuan_sock_set_flag (assuan_fd_t sockfd, const char *name, int value); int assuan_sock_get_flag (assuan_fd_t sockfd, const char *name, int *r_value); int assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); +assuan_fd_t assuan_sock_connect_byname (const char *host, unsigned short port, + int reserved, + const char *credentials, + unsigned int flags); int assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); int assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected); diff --git a/src/libassuan.def b/src/libassuan.def index 9f31c31..c320151 100644 --- a/src/libassuan.def +++ b/src/libassuan.def @@ -114,6 +114,7 @@ EXPORTS assuan_sock_set_sockaddr_un @93 assuan_sock_set_flag @94 assuan_sock_get_flag @95 + assuan_sock_connect_byname @96 ; END diff --git a/src/libassuan.vers b/src/libassuan.vers index 2b2389d..37c0131 100644 --- a/src/libassuan.vers +++ b/src/libassuan.vers @@ -104,6 +104,7 @@ LIBASSUAN_1.0 { assuan_sock_set_sockaddr_un; assuan_sock_set_flag; assuan_sock_get_flag; + assuan_sock_connect_byname; __assuan_close; __assuan_pipe; |