updates.
[silc.git] / lib / silcutil / silcsockconn.c
1 /*
2
3   silcsockconn.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23
24 /* Heartbeat context */
25 struct SilcSocketConnectionHBStruct {
26   uint32 heartbeat;
27   SilcSocketConnectionHBCb hb_callback;
28   void *hb_context;
29   void *timeout_queue;
30   SilcTask hb_task;
31   SilcSocketConnection sock;
32 };
33
34 /* Internal async host lookup context. */
35 typedef struct {
36   SilcSocketHostLookupCb callback;
37   void *context;
38   void *timeout_queue;
39   SilcSocketConnection sock;
40   bool port;
41 } *SilcSocketHostLookup;
42
43 /* Allocates a new socket connection object. The allocated object is 
44    returned to the new_socket argument. */
45
46 void silc_socket_alloc(int sock, SilcSocketType type, void *user_data, 
47                        SilcSocketConnection *new_socket)
48 {
49   SILC_LOG_DEBUG(("Allocating new socket connection object"));
50
51   /* Set the pointers. Incoming and outgoing data buffers
52      are allocated by the application when they are first used. */
53   *new_socket = silc_calloc(1, sizeof(**new_socket));
54   (*new_socket)->sock = sock;
55   (*new_socket)->type = type;
56   (*new_socket)->user_data = user_data;
57   (*new_socket)->protocol = NULL;
58   (*new_socket)->flags = 0;
59   (*new_socket)->inbuf = NULL;
60   (*new_socket)->outbuf = NULL;
61   (*new_socket)->users++;
62 }
63
64 /* Free's the Socket connection object. */
65
66 void silc_socket_free(SilcSocketConnection sock)
67 {
68   sock->users--;
69   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users + 1,
70                   sock->users));
71   if (sock->users < 1) {
72     silc_buffer_free(sock->inbuf);
73     silc_buffer_free(sock->outbuf);
74     if (sock->hb) {
75       silc_task_unregister(sock->hb->timeout_queue, sock->hb->hb_task);
76       silc_free(sock->hb->hb_context);
77       silc_free(sock->hb);
78     }
79
80     memset(sock, 'F', sizeof(*sock));
81     silc_free(sock);
82   }
83 }
84
85 /* Increase the reference counter. */
86
87 SilcSocketConnection silc_socket_dup(SilcSocketConnection sock)
88 {
89   sock->users++;
90   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users - 1,
91                   sock->users));
92   return sock;
93 }
94
95 /* Internal timeout callback to perform heartbeat */
96
97 SILC_TASK_CALLBACK(silc_socket_heartbeat)
98 {
99   SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context;
100
101   if (!hb->heartbeat)
102     return;
103
104   if (hb->hb_callback)
105     hb->hb_callback(hb->sock, hb->hb_context);
106
107   hb->hb_task = silc_task_register(hb->timeout_queue, hb->sock->sock, 
108                                    silc_socket_heartbeat,
109                                    context, hb->heartbeat, 0,
110                                    SILC_TASK_TIMEOUT,
111                                    SILC_TASK_PRI_LOW);
112 }
113
114 /* Sets the heartbeat timeout and prepares the socket for performing
115    heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
116    allocated by the application and will be sent as argument to the
117    `hb_callback' function that is called when the `heartbeat' timeout
118    expires.  The callback `hb_context' won't be touched by the library
119    but will be freed automatically when calling silc_socket_free.  The
120    `timeout_queue' is the application's scheduler timeout queue. */
121
122 void silc_socket_set_heartbeat(SilcSocketConnection sock, 
123                                uint32 heartbeat,
124                                void *hb_context,
125                                SilcSocketConnectionHBCb hb_callback,
126                                void *timeout_queue)
127 {
128
129   if (!timeout_queue)
130     return;
131
132   if (sock->hb) {
133     silc_task_unregister(sock->hb->timeout_queue, sock->hb->hb_task);
134     silc_free(sock->hb->hb_context);
135     silc_free(sock->hb);
136   }
137
138   sock->hb = silc_calloc(1, sizeof(*sock->hb));
139   sock->hb->heartbeat = heartbeat;
140   sock->hb->hb_context = hb_context;
141   sock->hb->hb_callback = hb_callback;
142   sock->hb->timeout_queue = timeout_queue;
143   sock->hb->sock = sock;
144   sock->hb->hb_task = silc_task_register(timeout_queue, sock->sock,
145                                          silc_socket_heartbeat,
146                                          (void *)sock->hb, heartbeat, 0,
147                                          SILC_TASK_TIMEOUT,
148                                          SILC_TASK_PRI_LOW);
149 }
150
151 /* Finishing timeout callback that will actually call the user specified
152    host lookup callback. This is executed back in the calling thread and
153    not in the lookup thread. */
154
155 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
156 {
157   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
158
159   SILC_UNSET_HOST_LOOKUP(lookup->sock);
160
161   /* If the reference counter is 1 we know that we are the only one
162      holding the socket and it thus is considered freed. The lookup
163      is cancelled also and we will not call the final callback. */
164   if (lookup->sock->users == 1) {
165     SILC_LOG_DEBUG(("Async host lookup was cancelled"));
166     silc_free(lookup);
167     silc_socket_free(lookup->sock);
168     return;
169   }
170
171   SILC_LOG_DEBUG(("Async host lookup finished"));
172
173   silc_socket_free(lookup->sock);
174
175   /* Call the final callback. */
176   if (lookup->callback)
177     lookup->callback(lookup->sock, lookup->context);
178
179   silc_free(lookup);
180 }
181
182 /* The thread function that performs the actual lookup. */
183
184 static void *silc_socket_host_lookup_start(void *context)
185 {
186   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
187   SilcSocketConnection sock = lookup->sock;
188
189   if (lookup->port)
190     sock->port = silc_net_get_remote_port(sock->sock);
191
192   silc_net_check_host_by_sock(sock->sock, &sock->hostname, &sock->ip);  
193   if (!sock->hostname && sock->ip)
194     sock->hostname = strdup(sock->ip);
195
196   silc_task_register(lookup->timeout_queue, sock->sock,
197                      silc_socket_host_lookup_finish, lookup, 0, 1,
198                      SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
199   silc_task_queue_wakeup(lookup->timeout_queue);
200
201   return NULL;
202 }
203
204 /* Performs asynchronous host name and IP address lookups for the
205    specified socket connection. This may be called when the socket
206    connection is created and the full IP address and fully qualified
207    domain name information is desired. The `callback' with `context'
208    will be called after the lookup is performed. The `timeout_queue'
209    is the application's scheduler timeout queue which the lookup
210    routine needs. If the socket connection is freed during
211    the lookup the library will automatically cancel the lookup and
212    the `callback' will not be called. */
213
214 void silc_socket_host_lookup(SilcSocketConnection sock,
215                              bool port_lookup,
216                              SilcSocketHostLookupCb callback,
217                              void *context,
218                              void *timeout_queue)
219 {
220   SilcSocketHostLookup lookup;
221
222   SILC_LOG_DEBUG(("Performing async host lookup"));
223
224   if (!timeout_queue)
225     return;
226
227   lookup = silc_calloc(1, sizeof(*lookup));
228   lookup->sock = silc_socket_dup(sock); /* Increase reference counter */
229   lookup->callback = callback;
230   lookup->context = context;
231   lookup->timeout_queue = timeout_queue;
232   lookup->port = port_lookup;
233
234   SILC_SET_HOST_LOOKUP(sock);
235
236 #ifdef SILC_THREADS
237   silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
238 #else
239   silc_socket_host_lookup_start((void *)lookup);
240 #endif
241 }