aboutsummaryrefslogtreecommitdiffstats
path: root/src/assuan-socket-connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/assuan-socket-connect.c')
-rw-r--r--src/assuan-socket-connect.c219
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);
}