Line data Source code
1 : /* assuan-socket-connect.c - Assuan socket based client
2 : Copyright (C) 2002, 2003, 2004, 2009 Free Software Foundation, Inc.
3 :
4 : This file is part of Assuan.
5 :
6 : Assuan is free software; you can redistribute it and/or modify it
7 : under the terms of the GNU Lesser General Public License as
8 : published by the Free Software Foundation; either version 2.1 of
9 : the License, or (at your option) any later version.
10 :
11 : Assuan is distributed in the hope that it will be useful, but
12 : WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : Lesser General Public License for more details.
15 :
16 : You should have received a copy of the GNU Lesser General Public
17 : License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include <config.h>
21 : #include <stdlib.h>
22 : #include <stddef.h>
23 : #include <stdio.h>
24 : #include <string.h>
25 : #include <errno.h>
26 : #ifdef HAVE_STDINT_H
27 : # include <stdint.h>
28 : #endif
29 : #ifdef HAVE_UNISTD_H
30 : # include <unistd.h>
31 : #endif
32 : #ifdef HAVE_SYS_TYPES_H
33 : # include <sys/types.h>
34 : #endif
35 : #ifdef HAVE_W32_SYSTEM
36 : # ifdef HAVE_WINSOCK2_H
37 : # include <winsock2.h>
38 : # endif
39 : # include <windows.h>
40 : #else
41 : # include <sys/socket.h>
42 : # include <sys/un.h>
43 : # include <netinet/in.h>
44 : # include <arpa/inet.h>
45 : #endif
46 :
47 : #include "assuan-defs.h"
48 : #include "debug.h"
49 :
50 : /* Hacks for Slowaris. */
51 : #ifndef PF_LOCAL
52 : # ifdef PF_UNIX
53 : # define PF_LOCAL PF_UNIX
54 : # else
55 : # define PF_LOCAL AF_UNIX
56 : # endif
57 : #endif
58 : #ifndef AF_LOCAL
59 : # define AF_LOCAL AF_UNIX
60 : #endif
61 : #ifndef INADDR_NONE
62 : #define INADDR_NONE ((unsigned long)(-1))
63 : #endif /*INADDR_NONE*/
64 :
65 : #ifndef SUN_LEN
66 : # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
67 : + strlen ((ptr)->sun_path))
68 : #endif
69 :
70 :
71 : #undef WITH_IPV6
72 : #if defined (AF_INET6) && defined(PF_INET) \
73 : && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON)
74 : # define WITH_IPV6 1
75 : #endif
76 :
77 :
78 :
79 : /* Returns true if STR represents a valid port number in decimal
80 : notation and no garbage is following. */
81 : static int
82 0 : parse_portno (const char *str, uint16_t *r_port)
83 : {
84 : unsigned int value;
85 :
86 0 : for (value=0; *str && (*str >= '0' && *str <= '9'); str++)
87 : {
88 0 : value = value * 10 + (*str - '0');
89 0 : if (value > 65535)
90 0 : return 0;
91 : }
92 0 : if (*str || !value)
93 0 : return 0;
94 :
95 0 : *r_port = value;
96 0 : return 1;
97 : }
98 :
99 :
100 : static gpg_error_t
101 0 : _assuan_connect_finalize (assuan_context_t ctx, assuan_fd_t fd,
102 : unsigned int flags)
103 : {
104 : gpg_error_t err;
105 :
106 0 : ctx->engine.release = _assuan_client_release;
107 0 : ctx->engine.readfnc = _assuan_simple_read;
108 0 : ctx->engine.writefnc = _assuan_simple_write;
109 0 : ctx->engine.sendfd = NULL;
110 0 : ctx->engine.receivefd = NULL;
111 0 : ctx->finish_handler = _assuan_client_finish;
112 0 : ctx->inbound.fd = fd;
113 0 : ctx->outbound.fd = fd;
114 0 : ctx->max_accepts = -1;
115 :
116 0 : if (flags & ASSUAN_SOCKET_CONNECT_FDPASSING)
117 0 : _assuan_init_uds_io (ctx);
118 :
119 : /* initial handshake */
120 : {
121 : assuan_response_t response;
122 : int off;
123 :
124 0 : err = _assuan_read_from_server (ctx, &response, &off, 0);
125 0 : if (err)
126 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
127 : "can't connect to server: %s\n", gpg_strerror (err));
128 0 : else if (response != ASSUAN_RESPONSE_OK)
129 : {
130 0 : char *sname = _assuan_encode_c_string (ctx, ctx->inbound.line);
131 0 : if (sname)
132 : {
133 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
134 : "can't connect to server: %s", sname);
135 0 : _assuan_free (ctx, sname);
136 : }
137 0 : err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED);
138 : }
139 : }
140 :
141 0 : return err;
142 : }
143 :
144 :
145 : /* Attach an existing connected file descriptor FD to an allocated handle CTX
146 : * and initialize the connection.
147 : */
148 : gpg_error_t
149 0 : assuan_socket_connect_fd (assuan_context_t ctx, int fd, unsigned int flags)
150 : {
151 : gpg_error_t err;
152 : assuan_fd_t afd;
153 :
154 0 : if (!ctx || fd < 0)
155 0 : return GPG_ERR_INV_ARG;
156 0 : afd = assuan_fd_from_posix_fd (fd);
157 0 : if (afd == ASSUAN_INVALID_FD)
158 0 : return GPG_ERR_INV_ARG;
159 :
160 0 : err = _assuan_connect_finalize(ctx, afd, flags);
161 :
162 0 : if (err)
163 0 : _assuan_reset (ctx);
164 :
165 0 : return err;
166 : }
167 :
168 :
169 : /* Make a connection to the Unix domain socket NAME and return a new
170 : Assuan context in CTX. SERVER_PID is currently not used but may
171 : become handy in the future. Defined flag bits are:
172 :
173 : ASSUAN_SOCKET_CONNECT_FDPASSING
174 : sendmsg and recvmsg are used.
175 :
176 : NAME must either start with a slash and optional with a drive
177 : prefix ("c:") or use one of these URL schemata:
178 :
179 : file://<fname>
180 :
181 : This is the same as the default just with an explicit schemata.
182 :
183 : assuan://<ipaddr>:<port>
184 : assuan://[<ip6addr>]:<port>
185 :
186 : Connect using TCP to PORT of the server with the numerical
187 : IPADDR. Note that '[' and ']' are literal characters.
188 :
189 : */
190 : gpg_error_t
191 0 : assuan_socket_connect (assuan_context_t ctx, const char *name,
192 : pid_t server_pid, unsigned int flags)
193 : {
194 0 : gpg_error_t err = 0;
195 : assuan_fd_t fd;
196 : #ifdef WITH_IPV6
197 : struct sockaddr_in6 srvr_addr_in6;
198 : #endif
199 : struct sockaddr_un srvr_addr_un;
200 : struct sockaddr_in srvr_addr_in;
201 0 : struct sockaddr *srvr_addr = NULL;
202 0 : uint16_t port = 0;
203 0 : size_t len = 0;
204 : const char *s;
205 0 : int af = AF_LOCAL;
206 0 : int pf = PF_LOCAL;
207 :
208 0 : TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx,
209 : "name=%s, flags=0x%x", name ? name : "(null)", flags);
210 :
211 0 : if (!ctx || !name)
212 0 : return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
213 :
214 0 : if (!strncmp (name, "file://", 7) && name[7])
215 0 : name += 7;
216 0 : else if (!strncmp (name, "assuan://", 9) && name[9])
217 : {
218 0 : name += 9;
219 0 : af = AF_INET;
220 0 : pf = PF_INET;
221 : }
222 : else /* Default. */
223 : {
224 : /* We require that the name starts with a slash if no URL
225 : schemata is used. To make things easier we allow an optional
226 : drive prefix. */
227 0 : s = name;
228 0 : if (*s && s[1] == ':')
229 0 : s += 2;
230 0 : if (*s != DIRSEP_C && *s != '/')
231 0 : return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
232 : }
233 :
234 0 : if (af == AF_LOCAL)
235 : {
236 : int redirected;
237 :
238 0 : if (_assuan_sock_set_sockaddr_un (name, (struct sockaddr *)&srvr_addr_un,
239 : &redirected))
240 0 : return _assuan_error (ctx, gpg_err_code_from_syserror ());
241 :
242 0 : len = SUN_LEN (&srvr_addr_un);
243 0 : srvr_addr = (struct sockaddr *)&srvr_addr_un;
244 : }
245 : else
246 : {
247 : char *addrstr, *p;
248 : #ifdef HAVE_INET_PTON
249 0 : void *addrbuf = NULL;
250 : #endif
251 :
252 0 : addrstr = _assuan_malloc (ctx, strlen (name) + 1);
253 0 : if (!addrstr)
254 0 : return _assuan_error (ctx, gpg_err_code_from_syserror ());
255 :
256 0 : if (*name == '[')
257 : {
258 0 : strcpy (addrstr, name+1);
259 0 : p = strchr (addrstr, ']');
260 0 : if (!p || p[1] != ':' || !parse_portno (p+2, &port))
261 0 : err = _assuan_error (ctx, GPG_ERR_BAD_URI);
262 : else
263 : {
264 0 : *p = 0;
265 : #ifdef WITH_IPV6
266 0 : af = AF_INET6;
267 0 : pf = PF_INET6;
268 0 : memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6);
269 0 : srvr_addr_in6.sin6_family = af;
270 0 : srvr_addr_in6.sin6_port = htons (port);
271 : #ifdef HAVE_INET_PTON
272 0 : addrbuf = &srvr_addr_in6.sin6_addr;
273 : #endif
274 0 : srvr_addr = (struct sockaddr *)&srvr_addr_in6;
275 0 : len = sizeof srvr_addr_in6;
276 : #else
277 : err = _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT);
278 : #endif
279 : }
280 : }
281 : else
282 : {
283 0 : strcpy (addrstr, name);
284 0 : p = strchr (addrstr, ':');
285 0 : if (!p || !parse_portno (p+1, &port))
286 0 : err = _assuan_error (ctx, GPG_ERR_BAD_URI);
287 : else
288 : {
289 0 : *p = 0;
290 0 : memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
291 0 : srvr_addr_in.sin_family = af;
292 0 : srvr_addr_in.sin_port = htons (port);
293 : #ifdef HAVE_INET_PTON
294 0 : addrbuf = &srvr_addr_in.sin_addr;
295 : #endif
296 0 : srvr_addr = (struct sockaddr *)&srvr_addr_in;
297 0 : len = sizeof srvr_addr_in;
298 : }
299 : }
300 :
301 0 : if (!err)
302 : {
303 : #ifdef HAVE_INET_PTON
304 0 : switch (inet_pton (af, addrstr, addrbuf))
305 : {
306 0 : case 1: break;
307 0 : case 0: err = _assuan_error (ctx, GPG_ERR_BAD_URI); break;
308 0 : default: err = _assuan_error (ctx, gpg_err_code_from_syserror ());
309 : }
310 : #else /*!HAVE_INET_PTON*/
311 : /* We need to use the old function. If we are here v6
312 : support isn't enabled anyway and thus we can do fine
313 : without. Note that Windows as a compatible inet_pton
314 : function named inetPton, but only since Vista. */
315 : srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr);
316 : if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE)
317 : err = _assuan_error (ctx, GPG_ERR_BAD_URI);
318 : #endif /*!HAVE_INET_PTON*/
319 : }
320 :
321 0 : _assuan_free (ctx, addrstr);
322 0 : if (err)
323 0 : return err;
324 : }
325 :
326 0 : fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0);
327 0 : if (fd == ASSUAN_INVALID_FD)
328 : {
329 0 : err = _assuan_error (ctx, gpg_err_code_from_syserror ());
330 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
331 : "can't create socket: %s", strerror (errno));
332 0 : return err;
333 : }
334 :
335 0 : if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1)
336 : {
337 0 : TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx,
338 : "can't connect to `%s': %s\n", name, strerror (errno));
339 0 : _assuan_close (ctx, fd);
340 0 : return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED);
341 : }
342 :
343 0 : err = _assuan_connect_finalize (ctx, fd, flags);
344 :
345 0 : if (err)
346 0 : _assuan_reset (ctx);
347 :
348 0 : return err;
349 : }
|