Integer type name change.
[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   SilcUInt32 heartbeat;
27   SilcSocketConnectionHBCb hb_callback;
28   void *hb_context;
29   SilcSchedule schedule;
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   SilcSchedule schedule;
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_schedule_task_del(sock->hb->schedule, sock->hb->hb_task);
76       silc_free(sock->hb->hb_context);
77       silc_free(sock->hb);
78     }
79
80     silc_free(sock->ip);
81     silc_free(sock->hostname);
82
83     memset(sock, 'F', sizeof(*sock));
84     silc_free(sock);
85   }
86 }
87
88 /* Increase the reference counter. */
89
90 SilcSocketConnection silc_socket_dup(SilcSocketConnection sock)
91 {
92   sock->users++;
93   SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users - 1,
94                   sock->users));
95   return sock;
96 }
97
98 /* Internal timeout callback to perform heartbeat */
99
100 SILC_TASK_CALLBACK(silc_socket_heartbeat)
101 {
102   SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context;
103
104   if (!hb->heartbeat)
105     return;
106
107   if (hb->hb_callback)
108     hb->hb_callback(hb->sock, hb->hb_context);
109
110   hb->hb_task = silc_schedule_task_add(hb->schedule, hb->sock->sock, 
111                                        silc_socket_heartbeat,
112                                        context, hb->heartbeat, 0,
113                                        SILC_TASK_TIMEOUT,
114                                        SILC_TASK_PRI_LOW);
115 }
116
117 /* Sets the heartbeat timeout and prepares the socket for performing
118    heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
119    allocated by the application and will be sent as argument to the
120    `hb_callback' function that is called when the `heartbeat' timeout
121    expires.  The callback `hb_context' won't be touched by the library
122    but will be freed automatically when calling silc_socket_free.  The
123    `schedule' is the application's scheduler. */
124
125 void silc_socket_set_heartbeat(SilcSocketConnection sock, 
126                                SilcUInt32 heartbeat,
127                                void *hb_context,
128                                SilcSocketConnectionHBCb hb_callback,
129                                SilcSchedule schedule)
130 {
131   if (sock->hb) {
132     silc_schedule_task_del(schedule, sock->hb->hb_task);
133     silc_free(sock->hb->hb_context);
134     silc_free(sock->hb);
135   }
136
137   sock->hb = silc_calloc(1, sizeof(*sock->hb));
138   sock->hb->heartbeat = heartbeat;
139   sock->hb->hb_context = hb_context;
140   sock->hb->hb_callback = hb_callback;
141   sock->hb->schedule = schedule;
142   sock->hb->sock = sock;
143   sock->hb->hb_task = silc_schedule_task_add(schedule, sock->sock,
144                                              silc_socket_heartbeat,
145                                              (void *)sock->hb, heartbeat, 0,
146                                              SILC_TASK_TIMEOUT,
147                                              SILC_TASK_PRI_LOW);
148 }
149
150 /* Finishing timeout callback that will actually call the user specified
151    host lookup callback. This is executed back in the calling thread and
152    not in the lookup thread. */
153
154 SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
155 {
156   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
157
158   SILC_UNSET_HOST_LOOKUP(lookup->sock);
159
160   /* If the reference counter is 1 we know that we are the only one
161      holding the socket and it thus is considered freed. The lookup
162      is cancelled also and we will not call the final callback. */
163   if (lookup->sock->users == 1) {
164     SILC_LOG_DEBUG(("Async host lookup was cancelled"));
165     silc_free(lookup);
166     silc_socket_free(lookup->sock);
167     return;
168   }
169
170   SILC_LOG_DEBUG(("Async host lookup finished"));
171
172   silc_socket_free(lookup->sock);
173
174   /* Call the final callback. */
175   if (lookup->callback)
176     lookup->callback(lookup->sock, lookup->context);
177
178   silc_free(lookup);
179 }
180
181 /* The thread function that performs the actual lookup. */
182
183 static void *silc_socket_host_lookup_start(void *context)
184 {
185   SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
186   SilcSocketConnection sock = lookup->sock;
187
188   if (lookup->port)
189     sock->port = silc_net_get_remote_port(sock->sock);
190
191   silc_net_check_host_by_sock(sock->sock, &sock->hostname, &sock->ip);  
192   if (!sock->hostname && sock->ip)
193     sock->hostname = strdup(sock->ip);
194
195   silc_schedule_task_add(lookup->schedule, sock->sock,
196                          silc_socket_host_lookup_finish, lookup, 0, 1,
197                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
198   silc_schedule_wakeup(lookup->schedule);
199
200   return NULL;
201 }
202
203 /* Performs asynchronous host name and IP address lookups for the
204    specified socket connection. This may be called when the socket
205    connection is created and the full IP address and fully qualified
206    domain name information is desired. The `callback' with `context'
207    will be called after the lookup is performed. The `schedule'
208    is the application's scheduler which the lookup routine needs. If
209    the socket connection is freed during the lookup the library will
210    automatically cancel the lookup and the `callback' will not be called. */
211
212 void silc_socket_host_lookup(SilcSocketConnection sock,
213                              bool port_lookup,
214                              SilcSocketHostLookupCb callback,
215                              void *context,
216                              SilcSchedule schedule)
217 {
218   SilcSocketHostLookup lookup;
219
220   SILC_LOG_DEBUG(("Performing async host lookup"));
221
222   lookup = silc_calloc(1, sizeof(*lookup));
223   lookup->sock = silc_socket_dup(sock); /* Increase reference counter */
224   lookup->callback = callback;
225   lookup->context = context;
226   lookup->schedule = schedule;
227   lookup->port = port_lookup;
228
229   SILC_SET_HOST_LOOKUP(sock);
230   silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
231 }