2 net-nonblock.c : Nonblocking net_connect()
4 Copyright (C) 1998-2000 Timo Sirainen
6 This program 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 This program 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 this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "net-nonblock.h"
38 #define is_fatal_error(err) \
39 (err != 0 && err != G_IO_ERROR_AGAIN && errno != EINTR)
41 static int g_io_channel_write_block(GIOChannel *channel, void *data, int len)
48 err = g_io_channel_write(channel, (char *) data + sent,
51 } while (sent < len && !is_fatal_error(err));
53 return err != 0 ? -1 : 0;
56 static int g_io_channel_read_block(GIOChannel *channel, void *data, int len)
62 maxwait = time(NULL)+2;
65 err = g_io_channel_read(channel, (char *) data + received,
68 } while (received < len && time(NULL) < maxwait &&
69 (ret != 0 || !is_fatal_error(err)));
71 return received < len ? -1 : 0;
74 /* nonblocking gethostbyname(), ip (IPADDR) + error (int, 0 = not error) is
75 written to pipe when found PID of the resolver child is returned */
76 int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
86 g_return_val_if_fail(addr != NULL, FALSE);
98 g_warning("net_connect_thread(): fork() failed! "
99 "Using blocking resolving");
104 memset(&rec, 0, sizeof(rec));
105 rec.error = net_gethostbyname(addr, &rec.ip4, &rec.ip6);
106 if (rec.error == 0) {
108 if (reverse_lookup) {
109 /* reverse lookup the IP, ignore any error */
110 if (rec.ip4.family != 0)
111 net_gethostbyaddr(&rec.ip4, &rec.host4);
112 if (rec.ip6.family != 0)
113 net_gethostbyaddr(&rec.ip6, &rec.host6);
116 errorstr = net_gethosterror(rec.error);
117 rec.errlen = errorstr == NULL ? 0 : strlen(errorstr)+1;
120 g_io_channel_write_block(pipe, &rec, sizeof(rec));
122 g_io_channel_write_block(pipe, (void *) errorstr, rec.errlen);
125 len = strlen(rec.host4) + 1;
126 g_io_channel_write_block(pipe, (void *) &len,
128 g_io_channel_write_block(pipe, (void *) rec.host4,
132 len = strlen(rec.host6) + 1;
133 g_io_channel_write_block(pipe, (void *) &len,
135 g_io_channel_write_block(pipe, (void *) rec.host6,
145 /* we used blocking lookup */
149 /* get the resolved IP address */
150 int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec)
155 rec->errorstr = NULL;
160 fcntl(g_io_channel_unix_get_fd(pipe), F_SETFL, O_NONBLOCK);
164 if (g_io_channel_read_block(pipe, rec, sizeof(*rec)) == -1) {
165 rec->errorstr = g_strdup_printf("Host name lookup: %s",
171 /* read error string, if we can't read everything for some
172 reason, just ignore it. */
173 rec->errorstr = g_malloc0(rec->errlen+1);
174 g_io_channel_read_block(pipe, rec->errorstr, rec->errlen);
177 g_io_channel_read_block(pipe, &len, sizeof(int));
178 rec->host4 = g_malloc0(len);
179 g_io_channel_read_block(pipe, rec->host4, len);
182 g_io_channel_read_block(pipe, &len, sizeof(int));
183 rec->host6 = g_malloc0(len);
184 g_io_channel_read_block(pipe, rec->host6, len);
191 /* Get host name, call func when finished */
192 int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data)
194 /* FIXME: not implemented */
198 /* Kill the resolver child */
199 void net_disconnect_nonblock(int pid)
201 g_return_if_fail(pid > 0);
208 static void simple_init(SIMPLE_THREAD_REC *rec, GIOChannel *handle)
210 g_return_if_fail(rec != NULL);
212 g_source_remove(rec->tag);
214 if (net_geterror(handle) != 0) {
216 g_io_channel_close(handle);
217 g_io_channel_unref(handle);
221 rec->func(handle, rec->data);
225 static void simple_readpipe(SIMPLE_THREAD_REC *rec, GIOChannel *pipe)
227 RESOLVED_IP_REC iprec;
231 g_return_if_fail(rec != NULL);
233 g_source_remove(rec->tag);
235 net_gethostbyname_return(pipe, &iprec);
236 g_free_not_null(iprec.errorstr);
238 g_io_channel_close(rec->pipes[0]);
239 g_io_channel_unref(rec->pipes[0]);
240 g_io_channel_close(rec->pipes[1]);
241 g_io_channel_unref(rec->pipes[1]);
243 ip = iprec.ip4.family != 0 ? &iprec.ip4 : &iprec.ip6;
244 handle = iprec.error == -1 ? NULL :
245 net_connect_ip(ip, rec->port, rec->my_ip);
247 g_free_not_null(rec->my_ip);
249 if (handle == NULL) {
251 rec->func(NULL, rec->data);
256 rec->tag = g_input_add(handle, G_INPUT_READ | G_INPUT_WRITE,
257 (GInputFunction) simple_init, rec);
260 /* Connect to server, call func when finished */
261 int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip,
262 NET_CALLBACK func, void *data)
264 SIMPLE_THREAD_REC *rec;
267 g_return_val_if_fail(server != NULL, FALSE);
268 g_return_val_if_fail(func != NULL, FALSE);
271 g_warning("net_connect_nonblock(): pipe() failed.");
275 rec = g_new0(SIMPLE_THREAD_REC, 1);
278 rec->my_ip = g_malloc(sizeof(IPADDR));
279 memcpy(rec->my_ip, my_ip, sizeof(IPADDR));
283 rec->pipes[0] = g_io_channel_unix_new(fd[0]);
284 rec->pipes[1] = g_io_channel_unix_new(fd[1]);
286 /* start nonblocking host name lookup */
287 net_gethostbyname_nonblock(server, rec->pipes[1], 0);
288 rec->tag = g_input_add(rec->pipes[0], G_INPUT_READ,
289 (GInputFunction) simple_readpipe, rec);