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