diff options
Diffstat (limited to 'src/assuan-socket-connect.c')
-rw-r--r-- | src/assuan-socket-connect.c | 219 |
1 files changed, 184 insertions, 35 deletions
diff --git a/src/assuan-socket-connect.c b/src/assuan-socket-connect.c index 3b20f19..13b8aa5 100644 --- a/src/assuan-socket-connect.c +++ b/src/assuan-socket-connect.c @@ -23,13 +23,18 @@ #include <stdio.h> #include <string.h> #include <errno.h> +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif #include <unistd.h> #include <sys/types.h> -#ifndef HAVE_W32_SYSTEM -#include <sys/socket.h> -#include <sys/un.h> +#ifdef HAVE_W32_SYSTEM +# include <windows.h> #else -#include <windows.h> +# include <sys/socket.h> +# include <sys/un.h> +# include <netinet/in.h> +# include <arpa/inet.h> #endif #include "assuan-defs.h" @@ -53,19 +58,74 @@ #endif +#undef WITH_IPV6 +#if defined (AF_INET6) && defined(PF_INET) \ + && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON) +# define WITH_IPV6 1 +#endif + + + +/* Returns true if STR represents a valid port number in decimal + notation and no garbage is following. */ +static int +parse_portno (const char *str, uint16_t *r_port) +{ + unsigned int value; + + for (value=0; *str && (*str >= '0' && *str <= '9'); str++) + { + value = value * 10 + (*str - '0'); + if (value > 65535) + return 0; + } + if (*str || !value) + return 0; + + *r_port = value; + return 1; +} + + + /* Make a connection to the Unix domain socket NAME and return a new Assuan context in CTX. SERVER_PID is currently not used but may - become handy in the future. With flags set to 1 sendmsg and - recvmsg are used. */ + become handy in the future. Defined flag bits are: + + ASSUAN_SOCKET_CONNECT_FDPASSING + sendmsg and recvmsg are used. + + NAME must either start with a slash and optional with a drive + prefix ("c:") or use one of these URL schemata: + + file://<fname> + + This is the same as the defualt just with an explicit schemata. + + assuan://<ipaddr>:<port> + assuan://[<ip6addr>]:<port> + + Connect using TCP to PORT of the server with the numerical + IPADDR. Not that the '[' and ']' are literal characters. + + */ gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name, pid_t server_pid, unsigned int flags) { - gpg_error_t err; + gpg_error_t err = 0; assuan_fd_t fd; - struct sockaddr_un srvr_addr; - size_t len; +#ifdef WITH_IPV6 + struct sockaddr_in6 srvr_addr_in6; +#endif + struct sockaddr_un srvr_addr_un; + struct sockaddr_in srvr_addr_in; + struct sockaddr *srvr_addr = NULL; + uint16_t port = 0; + size_t len = 0; const char *s; + int af = AF_LOCAL; + int pf = PF_LOCAL; TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx, "name=%s, flags=0x%x", name ? name : "(null)", flags); @@ -73,38 +133,127 @@ assuan_socket_connect (assuan_context_t ctx, const char *name, if (!ctx || !name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); - /* We require that the name starts with a slash, so that we - eventually can reuse this function for other socket types. To - make things easier we allow an optional driver prefix. */ - s = name; - if (*s && s[1] == ':') - s += 2; - if (*s != DIRSEP_C && *s != '/') - return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); + if (!strncmp (name, "file://", 7) && name[7]) + name += 7; + else if (!strncmp (name, "assuan://", 9) && name[9]) + { + name += 9; + af = AF_INET; + pf = PF_INET; + } + else /* Default. */ + { + /* We require that the name starts with a slash if no URL + schemata is used. To make things easier we allow an optional + driver prefix. */ + s = name; + if (*s && s[1] == ':') + s += 2; + if (*s != DIRSEP_C && *s != '/') + return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); + } - if (strlen (name)+1 >= sizeof srvr_addr.sun_path) - return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); + if (af == AF_LOCAL) + { + if (strlen (name)+1 >= sizeof srvr_addr_un.sun_path) + return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); - fd = _assuan_sock_new (ctx, PF_LOCAL, SOCK_STREAM, 0); + memset (&srvr_addr_un, 0, sizeof srvr_addr_un); + srvr_addr_un.sun_family = AF_LOCAL; + strncpy (srvr_addr_un.sun_path, name, sizeof (srvr_addr_un.sun_path) - 1); + srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path) - 1] = 0; + len = SUN_LEN (&srvr_addr_un); + + srvr_addr = (struct sockaddr *)&srvr_addr_un; + } + else + { + char *addrstr, *p; + void *addrbuf = NULL; + + addrstr = _assuan_malloc (ctx, strlen (name) + 1); + if (!addrstr) + return _assuan_error (ctx, gpg_err_code_from_syserror ()); + + if (*addrstr == '[') + { + strcpy (addrstr, name+1); + p = strchr (addrstr, ']'); + if (!p || p[1] != ':' || !parse_portno (p+2, &port)) + err = _assuan_error (ctx, GPG_ERR_BAD_URI); + else + { + *p = 0; +#ifdef WITH_IPV6 + af = AF_INET6; + pf = PF_INET6; + memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); + srvr_addr_in6.sin6_family = af; + srvr_addr_in6.sin6_port = htons (port); + addrbuf = &srvr_addr_in6.sin6_addr; + srvr_addr = (struct sockaddr *)&srvr_addr_in6; + len = sizeof srvr_addr_in6; +#else + err = _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT); +#endif + } + } + else + { + strcpy (addrstr, name); + p = strchr (addrstr, ':'); + if (!p || !parse_portno (p+1, &port)) + err = _assuan_error (ctx, GPG_ERR_BAD_URI); + else + { + *p = 0; + memset (&srvr_addr_in, 0, sizeof srvr_addr_in); + srvr_addr_in.sin_family = af; + srvr_addr_in.sin_port = htons (port); + addrbuf = &srvr_addr_in.sin_addr; + srvr_addr = (struct sockaddr *)&srvr_addr_in; + len = sizeof srvr_addr_in; + } + } + + if (!err) + { +#ifdef HAVE_INET_PTON + switch (inet_pton (af, addrstr, addrbuf)) + { + case 1: break; + case 0: err = _assuan_error (ctx, GPG_ERR_BAD_URI); break; + default: err = _assuan_error (ctx, gpg_err_code_from_syserror ()); + } +#else /*!HAVE_INET_PTON*/ + /* We need to use the old function. If we are here v6 + support isn't enabled anyway and thus we can do fine + without. Note that Windows as a compatible inet_pton + function named inetPton, but only since Vista. */ + srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); + if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) + err = _assuan_error (ctx, GPG_ERR_BAD_URI); +#endif /*!HAVE_INET_PTON*/ + } + + _assuan_free (ctx, addrstr); + if (err) + return err; + } + + fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx, - "can't create socket: %s", strerror (errno)); - /* FIXME: Cleanup */ - return _assuan_error (ctx, GPG_ERR_ASS_GENERAL); + err = _assuan_error (ctx, gpg_err_code_from_syserror ()); + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, + "can't create socket: %s", strerror (errno)); + return err; } - memset (&srvr_addr, 0, sizeof srvr_addr); - srvr_addr.sun_family = AF_LOCAL; - strncpy (srvr_addr.sun_path, name, sizeof (srvr_addr.sun_path) - 1); - srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0; - len = SUN_LEN (&srvr_addr); - - if (_assuan_sock_connect (ctx, fd, (struct sockaddr *) &srvr_addr, len) == -1) + if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1) { - TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx, + TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to `%s': %s\n", name, strerror (errno)); - /* FIXME: Cleanup */ _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } @@ -129,14 +278,14 @@ assuan_socket_connect (assuan_context_t ctx, const char *name, err = _assuan_read_from_server (ctx, &response, &off); if (err) - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx, + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s\n", gpg_strerror (err)); else if (response != ASSUAN_RESPONSE_OK) { char *sname = _assuan_encode_c_string (ctx, ctx->inbound.line); if (sname) { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect_ext", ctx, + TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s", sname); _assuan_free (ctx, sname); } |