/* 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. 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 default just with an explicit schemata. assuan://<ipaddr>:<port> assuan://[<ip6addr>]:<port> Connect using TCP to PORT of the server with the numerical IPADDR. Note that '[' 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 = 0; assuan_fd_t fd; #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); if (!ctx || !name) 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 drive prefix. */ s = name; if (*s && s[1] == ':') s += 2; if (*s != DIRSEP_C && *s != '/') 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); 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; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif addrstr = _assuan_malloc (ctx, strlen (name) + 1); if (!addrstr) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (*name == '[') { 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); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif 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); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif 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) { 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; } if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1) { TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to `%s': %s\n", name, strerror (errno)); _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; }
int assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { return _assuan_sock_connect (sock_ctx, sockfd, addr, addrlen); }
/* 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 recvmesg are used. */ assuan_error_t assuan_socket_connect_ext(assuan_context_t *r_ctx, const char *name, pid_t server_pid, unsigned int flags) { static struct assuan_io io = { _assuan_simple_read, _assuan_simple_write }; assuan_error_t err; assuan_context_t ctx; int fd; struct sockaddr_un srvr_addr; size_t len; const char *s; if(!r_ctx || !name) return _assuan_error(ASSUAN_Invalid_Value); *r_ctx = NULL; /* 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 dirver prefix. */ s = name; if(*s && s[1] == ':') s += 2; if(*s != DIRSEP_C && *s != '/') return _assuan_error(ASSUAN_Invalid_Value); if(strlen(name) + 1 >= sizeof srvr_addr.sun_path) return _assuan_error(ASSUAN_Invalid_Value); err = _assuan_new_context(&ctx); if(err) return err; ctx->deinit_handler = ((flags & 1)) ? _assuan_uds_deinit : do_deinit; ctx->finish_handler = do_finish; fd = _assuan_sock_new(PF_LOCAL, SOCK_STREAM, 0); if(fd == -1) { _assuan_log_printf("can't create socket: %s\n", strerror(errno)); _assuan_release_context(ctx); return _assuan_error(ASSUAN_General_Error); } 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(fd, (struct sockaddr *) &srvr_addr, len) == -1) { _assuan_log_printf("can't connect to `%s': %s\n", name, strerror(errno)); _assuan_release_context(ctx); _assuan_close(fd); return _assuan_error(ASSUAN_Connect_Failed); } ctx->inbound.fd = fd; ctx->outbound.fd = fd; ctx->io = &io; if((flags & 1)) _assuan_init_uds_io(ctx); /* initial handshake */ { int okay, off; err = _assuan_read_from_server(ctx, &okay, &off); if(err) _assuan_log_printf("can't connect to server: %s\n", assuan_strerror(err)); else if(okay != 1) { /*LOG ("can't connect to server: `");*/ _assuan_log_sanitized_string(ctx->inbound.line); fprintf(assuan_get_assuan_log_stream(), "'\n"); err = _assuan_error(ASSUAN_Connect_Failed); } } if(err) { assuan_disconnect(ctx); } else *r_ctx = ctx; return 0; }