Add old stuff.
[shishi.git] / lib / netio.c
blobdb13599a12af77958f18a1e8f0f1d6b650a307cc
1 /* netio.c --- Network I/O functions.
2 * Copyright (C) 2002, 2003, 2004, 2006 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Shishi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "internal.h"
24 /* Get _shishi_sendrecv_tls, etc. */
25 #include "starttls.h"
27 /* Get _shishi_realminfo, etc. */
28 #include "diskio.h"
30 /* Get _shishi_realminfo. */
31 #include "cfg.h"
33 static int
34 shishi_sendrecv_udp (Shishi * handle,
35 struct sockaddr *addr,
36 const char *indata, int inlen,
37 char **outdata, size_t * outlen, size_t timeout)
39 struct sockaddr lsa;
40 struct sockaddr_in *lsa_inp = (struct sockaddr_in *) &lsa;
41 char tmpbuf[BUFSIZ]; /* XXX can we do without it?
42 MSG_PEEK|MSG_TRUNC doesn't work for udp.. */
43 int sockfd;
44 int bytes_sent;
45 struct sockaddr_storage from_sa;
46 socklen_t length = sizeof (struct sockaddr_storage);
47 fd_set readfds;
48 struct timeval tout;
49 int rc;
51 memset (&lsa, 0, sizeof (lsa));
52 lsa_inp->sin_family = AF_INET;
53 lsa_inp->sin_addr.s_addr = htonl (INADDR_ANY);
55 sockfd = socket (AF_INET, SOCK_DGRAM, 0);
56 if (sockfd < 0)
58 shishi_error_set (handle, strerror (errno));
59 return SHISHI_SOCKET_ERROR;
62 if (bind (sockfd, (struct sockaddr *) &lsa, sizeof (lsa)) != 0)
64 shishi_error_set (handle, strerror (errno));
65 close (sockfd);
66 return SHISHI_BIND_ERROR;
69 bytes_sent = sendto (sockfd, (const void *) indata, inlen,
70 0, addr, sizeof (*addr));
71 if (bytes_sent != inlen)
73 shishi_error_set (handle, strerror (errno));
74 return SHISHI_SENDTO_ERROR;
77 FD_ZERO (&readfds);
78 FD_SET (sockfd, &readfds);
79 tout.tv_sec = timeout;
80 tout.tv_usec = 0;
81 if ((rc = select (sockfd + 1, &readfds, NULL, NULL, &tout)) != 1)
83 if (rc == -1)
84 shishi_error_set (handle, strerror (errno));
85 else
86 shishi_error_clear (handle);
87 return SHISHI_KDC_TIMEOUT;
90 *outlen = sizeof (tmpbuf);
91 *outlen = recvfrom (sockfd, tmpbuf, *outlen, 0,
92 (struct sockaddr *) &from_sa, &length);
94 if (*outlen == -1)
96 shishi_error_set (handle, strerror (errno));
97 return SHISHI_RECVFROM_ERROR;
100 *outdata = xmalloc (*outlen);
101 memcpy (*outdata, tmpbuf, *outlen);
103 if (close (sockfd) != 0)
105 shishi_error_set (handle, strerror (errno));
106 return SHISHI_CLOSE_ERROR;
109 return SHISHI_OK;
112 static int
113 shishi_sendrecv_tcp (Shishi * handle,
114 struct sockaddr *addr,
115 const char *indata, int inlen,
116 char **outdata, size_t * outlen, size_t timeout)
118 char tmpbuf[BUFSIZ]; /* XXX can we do without it?
119 MSG_PEEK|MSG_TRUNC doesn't work for udp.. */
120 int sockfd;
121 int bytes_sent;
122 struct sockaddr_storage from_sa;
123 socklen_t length = sizeof (struct sockaddr_storage);
124 fd_set readfds;
125 struct timeval tout;
126 int rc;
128 sockfd = socket (AF_INET, SOCK_STREAM, 0);
129 if (sockfd < 0)
131 shishi_error_set (handle, strerror (errno));
132 return SHISHI_SOCKET_ERROR;
135 if (connect (sockfd, addr, sizeof (*addr)) != 0)
137 shishi_error_set (handle, strerror (errno));
138 close (sockfd);
139 return SHISHI_CONNECT_ERROR;
142 tmpbuf[3] = inlen & 0xFF;
143 tmpbuf[2] = (inlen >> 8) & 0xFF;
144 tmpbuf[1] = (inlen >> 16) & 0xFF;
145 tmpbuf[0] = (inlen >> 24) & 0xFF;
147 bytes_sent = write (sockfd, tmpbuf, 4);
149 bytes_sent = write (sockfd, (const void *) indata, inlen);
150 if (bytes_sent != inlen)
152 shishi_error_set (handle, strerror (errno));
153 return SHISHI_SENDTO_ERROR;
156 FD_ZERO (&readfds);
157 FD_SET (sockfd, &readfds);
158 tout.tv_sec = timeout;
159 tout.tv_usec = 0;
160 if ((rc = select (sockfd + 1, &readfds, NULL, NULL, &tout)) != 1)
162 if (rc == -1)
163 shishi_error_set (handle, strerror (errno));
164 else
165 shishi_error_clear (handle);
166 return SHISHI_KDC_TIMEOUT;
169 *outlen = 4;
170 *outlen = recvfrom (sockfd, tmpbuf, *outlen, 0,
171 (struct sockaddr *) &from_sa, &length);
172 if (*outlen == -1)
174 shishi_error_set (handle, strerror (errno));
175 return SHISHI_RECVFROM_ERROR;
178 *outlen = sizeof (tmpbuf);
179 *outlen = recvfrom (sockfd, tmpbuf, *outlen, 0,
180 (struct sockaddr *) &from_sa, &length);
182 *outdata = xmalloc (*outlen);
183 memcpy (*outdata, tmpbuf, *outlen);
185 if (close (sockfd) != 0)
187 shishi_error_set (handle, strerror (errno));
188 return SHISHI_CLOSE_ERROR;
191 return SHISHI_OK;
194 static int
195 shishi_kdc_sendrecv_1 (Shishi * handle, struct Shishi_kdcinfo *ki,
196 const char *indata, size_t inlen,
197 char **outdata, size_t * outlen,
198 Shishi_tkts_hint * hint)
200 const char *protname;
201 int rc;
203 switch (ki->protocol)
205 #ifdef USE_STARTTLS
206 case TLS:
207 protname = "tls";
208 break;
209 #endif
211 case TCP:
212 protname = "tcp";
213 break;
215 default:
216 case UDP:
217 protname = "udp";
218 break;
221 shishi_verbose (handle, "Sending to %s (%s) via %s", ki->name,
222 inet_ntoa (((struct sockaddr_in *)
223 &ki->sockaddress)->sin_addr),
224 protname);
226 switch (ki->protocol)
228 #ifdef USE_STARTTLS
229 case TLS:
230 rc = _shishi_sendrecv_tls (handle, &ki->sockaddress,
231 indata, inlen, outdata, outlen,
232 handle->kdctimeout, hint);
233 break;
234 #endif
236 case TCP:
237 rc = shishi_sendrecv_tcp (handle, &ki->sockaddress,
238 indata, inlen, outdata, outlen,
239 handle->kdctimeout);
240 break;
242 case UDP:
243 default:
244 rc = shishi_sendrecv_udp (handle, &ki->sockaddress,
245 indata, inlen, outdata, outlen,
246 handle->kdctimeout);
247 break;
250 return rc;
253 static int
254 shishi_kdc_sendrecv_static (Shishi * handle, char *realm,
255 const char *indata, size_t inlen,
256 char **outdata, size_t * outlen,
257 Shishi_tkts_hint * hint)
259 struct Shishi_realminfo *ri;
260 size_t j, k;
261 int rc;
263 ri = _shishi_realminfo (handle, realm);
264 if (!ri)
266 shishi_error_printf (handle, "No KDC defined for realm %s", realm);
267 return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
270 for (j = 0; j < handle->kdcretries; j++)
271 for (k = 0; k < ri->nkdcaddresses; k++)
273 rc = shishi_kdc_sendrecv_1 (handle, &ri->kdcaddresses[k],
274 indata, inlen, outdata, outlen, hint);
275 if (rc != SHISHI_KDC_TIMEOUT)
276 return rc;
279 shishi_error_clear (handle);
280 return SHISHI_KDC_TIMEOUT;
283 static int
284 shishi_kdc_sendrecv_srv_1 (Shishi * handle, char *realm,
285 const char *indata, size_t inlen,
286 char **outdata, size_t * outlen, Shishi_dns rrs)
288 int rc;
290 for (; rrs; rrs = rrs->next)
292 Shishi_dns_srv srv = rrs->rr;
293 struct addrinfo hints;
294 struct addrinfo *ai;
295 char *port;
297 if (rrs->class != C_IN)
298 continue;
299 if (rrs->type != T_SRV)
300 continue;
302 shishi_verbose (handle, "Located SRV RRs server %s:%d",
303 srv->name, srv->port);
305 memset (&hints, 0, sizeof (hints));
306 hints.ai_socktype = SOCK_DGRAM;
307 port = xasprintf ("%d", srv->port);
308 rc = getaddrinfo (srv->name, port, &hints, &ai);
309 free (port);
311 if (rc != 0)
313 shishi_warn (handle, "Unknown KDC host `%s' (gai rc %d)",
314 srv->name, rc);
315 continue;
318 shishi_verbose (handle, "Sending to %s:%d (%s)",
319 srv->name, srv->port,
320 inet_ntoa (((struct sockaddr_in *)
321 ai->ai_addr)->sin_addr));
323 rc = shishi_sendrecv_udp (handle, ai->ai_addr,
324 indata, inlen, outdata, outlen,
325 handle->kdctimeout);
327 freeaddrinfo (ai);
329 if (rc != SHISHI_KDC_TIMEOUT)
330 return rc;
333 return SHISHI_KDC_TIMEOUT;
336 static int
337 shishi_kdc_sendrecv_srv (Shishi * handle, char *realm,
338 const char *indata, size_t inlen,
339 char **outdata, size_t * outlen)
341 Shishi_dns rrs;
342 char *tmp;
343 int rc;
345 shishi_verbose (handle, "Finding SRV RRs for %s", realm);
347 tmp = xasprintf ("_kerberos._udp.%s", realm);
348 rrs = shishi_resolv (tmp, SHISHI_DNS_SRV);
349 free (tmp);
351 if (rrs)
352 rc = shishi_kdc_sendrecv_srv_1 (handle, realm, indata, inlen,
353 outdata, outlen, rrs);
354 else
356 shishi_error_printf (handle, "No KDC SRV RRs for realm %s", realm);
357 rc = SHISHI_KDC_NOT_KNOWN_FOR_REALM;
360 shishi_resolv_free (rrs);
362 return rc;
365 static int
366 shishi_kdc_sendrecv_direct (Shishi * handle, char *realm,
367 const char *indata, size_t inlen,
368 char **outdata, size_t * outlen)
370 struct servent *se;
371 struct addrinfo hints;
372 struct addrinfo *ai;
373 char *port;
374 int rc;
376 shishi_verbose (handle, "Trying direct realm host mapping for %s", realm);
378 se = getservbyname ("kerberos", NULL);
379 if (se)
380 port = xasprintf ("%d", ntohs (se->s_port));
381 else
382 port = xasprintf ("%d", 88);
384 memset (&hints, 0, sizeof (hints));
385 hints.ai_socktype = SOCK_DGRAM;
386 rc = getaddrinfo (realm, port, &hints, &ai);
388 free (port);
390 if (rc != 0)
392 shishi_error_printf (handle, "No direct realm host for realm %s",
393 realm);
394 return SHISHI_KDC_NOT_KNOWN_FOR_REALM;
397 shishi_verbose (handle, "Sending to %s:%d (%s)", realm, port,
398 inet_ntoa (((struct sockaddr_in *) ai->ai_addr)->sin_addr));
400 rc = shishi_sendrecv_udp (handle, ai->ai_addr,
401 indata, inlen, outdata, outlen,
402 handle->kdctimeout);
404 freeaddrinfo (ai);
406 return rc;
410 shishi_kdc_sendrecv_hint (Shishi * handle, char *realm,
411 const char *indata, size_t inlen,
412 char **outdata, size_t * outlen,
413 Shishi_tkts_hint * hint)
415 int rc;
417 rc = shishi_kdc_sendrecv_static (handle, realm, indata, inlen,
418 outdata, outlen, hint);
419 if (rc == SHISHI_KDC_TIMEOUT || rc == SHISHI_KDC_NOT_KNOWN_FOR_REALM)
420 rc = shishi_kdc_sendrecv_srv (handle, realm,
421 indata, inlen, outdata, outlen);
422 if (rc == SHISHI_KDC_TIMEOUT || rc == SHISHI_KDC_NOT_KNOWN_FOR_REALM)
423 rc = shishi_kdc_sendrecv_direct (handle, realm,
424 indata, inlen, outdata, outlen);
426 return rc;
430 shishi_kdc_sendrecv (Shishi * handle, char *realm,
431 const char *indata, size_t inlen,
432 char **outdata, size_t * outlen)
434 return shishi_kdc_sendrecv_hint (handle, realm, indata, inlen,
435 outdata, outlen, NULL);