Bump copyright year
[pazpar2-moved-to-github.git] / src / getaddrinfo.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2010 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include "sel_thread.h"
25
26 #if HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #include <stdlib.h>
30
31 #include <assert.h>
32 #include <sys/types.h>
33 #if HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif
36 #ifdef WIN32
37 #include <winsock.h>
38 #endif
39 #if HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 #if HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45
46 #include <yaz/log.h>
47 #include <yaz/nmem.h>
48 #include <yaz/tcpip.h>
49
50 #include "pazpar2.h"
51 #include "connection.h"
52 #include "host.h"
53
54 /* Only use a threaded resolver on Unix that offers getaddrinfo.
55    gethostbyname is NOT reentrant.
56  */
57 #if HAVE_GETADDRINFO
58 #ifndef WIN32
59 #define USE_THREADED_RESOLVER 1
60 #endif
61 #endif
62
63 struct work {
64     char *hostport;  /* hostport to be resolved in separate thread */
65     char *ipport;    /* result or NULL if it could not be resolved */
66     struct host *host; /* host that we're dealing with - mother thread */
67 };
68
69 static int log_level = YLOG_LOG;
70
71 void perform_getaddrinfo(struct work *w)
72 {
73     int res = 0;
74     char *port;
75 #if HAVE_GETADDRINFO
76     struct addrinfo *addrinfo, hints;
77 #else
78     struct hostent *hp;
79 #endif
80     char *hostport = xstrdup(w->hostport);
81     
82     if ((port = strchr(hostport, ':')))
83         *(port++) = '\0';
84     else
85         port = "210";
86
87 #if HAVE_GETADDRINFO
88     hints.ai_flags = 0;
89     hints.ai_family = PF_INET;
90     hints.ai_socktype = SOCK_STREAM;
91     hints.ai_protocol = IPPROTO_TCP;
92     hints.ai_addrlen = 0;
93     hints.ai_addr = 0;
94     hints.ai_canonname = 0;
95     hints.ai_next = 0;
96     // This is not robust code. It assumes that getaddrinfo always
97     // returns AF_INET address.
98     if ((res = getaddrinfo(hostport, port, &hints, &addrinfo)))
99     {
100         yaz_log(YLOG_WARN, "Failed to resolve %s: %s", 
101                 w->hostport, gai_strerror(res));
102     }
103     else
104     {
105         char ipport[128];
106         unsigned char addrbuf[4];
107         assert(addrinfo->ai_family == PF_INET);
108         memcpy(addrbuf, 
109                &((struct sockaddr_in*)addrinfo->ai_addr)->sin_addr.s_addr, 4);
110         sprintf(ipport, "%u.%u.%u.%u:%s",
111                 addrbuf[0], addrbuf[1], addrbuf[2], addrbuf[3], port);
112         freeaddrinfo(addrinfo);
113         w->ipport = xstrdup(ipport);
114         yaz_log(log_level, "Resolved %s -> %s", hostport, ipport);
115     }
116 #else
117     hp = gethostbyname(hostport);
118     if (!hp)
119     {
120         yaz_log(YLOG_WARN|YLOG_ERRNO, "Failed to resolve %s", hostport);
121     }
122     else
123     {
124         char ipport[128];
125         unsigned char addrbuf[4];
126
127         memcpy(addrbuf, *hp->h_addr_list, 4 * sizeof(unsigned char));
128         sprintf(ipport, "%u.%u.%u.%u:%s",
129                 addrbuf[0], addrbuf[1], addrbuf[2], addrbuf[3], port);
130         w->ipport = xstrdup(ipport);
131         yaz_log(log_level, "Resolved %s -> %s", hostport, ipport);
132     }
133 #endif
134     xfree(hostport);
135 }
136
137 static void work_handler(void *vp)
138 {
139     struct work *w = vp;
140
141     int sec = 0;  /* >0 for debugging/testing purposes */
142     if (sec)
143     {
144         yaz_log(log_level, "waiting %d seconds", sec);
145 #if HAVE_UNISTD_H
146         sleep(sec);
147 #endif
148     }
149     perform_getaddrinfo(w);
150 }
151
152 #if USE_THREADED_RESOLVER
153 void iochan_handler(struct iochan *i, int event)
154 {
155     sel_thread_t p = iochan_getdata(i);
156
157     if (event & EVENT_INPUT)
158     {
159         struct work *w = sel_thread_result(p);
160         w->host->ipport = w->ipport;
161         connect_resolver_host(w->host);
162         xfree(w);
163     }
164 }
165
166 static sel_thread_t resolver_thread = 0;
167
168 static void getaddrinfo_start(void)
169 {
170     int fd;
171     sel_thread_t p = resolver_thread = 
172         sel_thread_create(work_handler, 0 /* work_destroy */, &fd,
173                           3 /* no of resolver threads */);
174     if (!p)
175     {
176         yaz_log(YLOG_FATAL|YLOG_ERRNO, "sel_create_create failed");
177         exit(1);
178     }
179     else
180     {
181         IOCHAN chan = iochan_create(fd, iochan_handler, EVENT_INPUT);
182         iochan_setdata(chan, p);
183         pazpar2_add_channel(chan);
184     }
185     yaz_log(log_level, "resolver start");
186     resolver_thread = p;
187 }
188 #endif
189
190 int host_getaddrinfo(struct host *host)
191 {
192     struct work *w = xmalloc(sizeof(*w));
193     int use_thread = 1; /* =0 to disable threading entirely */
194
195     w->hostport = host->hostport;
196     w->ipport = 0;
197     w->host = host;
198 #if USE_THREADED_RESOLVER
199     if (use_thread)
200     {
201         if (resolver_thread == 0)
202             getaddrinfo_start();
203         assert(resolver_thread);
204         sel_thread_add(resolver_thread, w);
205         return 0;
206     }
207 #endif
208     perform_getaddrinfo(w);
209     host->ipport = w->ipport;
210     xfree(w);
211     if (!host->ipport)
212         return -1;
213     return 0;
214 }
215
216 /*
217  * Local variables:
218  * c-basic-offset: 4
219  * c-file-style: "Stroustrup"
220  * indent-tabs-mode: nil
221  * End:
222  * vim: shiftwidth=4 tabstop=8 expandtab
223  */
224