26d9dd73a15e362f138f25281288c11f059ae815
[silc.git] / lib / silcutil / unix / silcunixsockconn.c
1 /*
2
3   silcunixsockconn.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 /* Writes data from encrypted buffer to the socket connection. If the
25    data cannot be written at once, it will be written later with a timeout. 
26    The data is written from the data section of the buffer, not from head
27    or tail section. This automatically pulls the data section towards end
28    after writing the data. */
29
30 int silc_socket_write(SilcSocketConnection sock)
31 {
32   int ret = 0;
33   int fd = sock->sock;
34   SilcBuffer src = sock->outbuf;
35
36   if (!src)
37     return -2;
38   if (SILC_IS_DISABLED(sock))
39     return -1;
40
41   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
42
43   if (src->len > 0) {
44     ret = write(fd, src->data, src->len);
45     if (ret < 0) {
46       if (errno == EAGAIN || errno == EINTR) {
47         SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
48         return -2;
49       }
50       SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
51       sock->sock_error = errno;
52       return -1;
53     }
54
55     if (ret < src->len) {
56       SILC_LOG_DEBUG(("Wrote data %d of %d bytes, will write rest later",
57                       ret, src->len));
58       silc_buffer_pull(src, ret);
59       return -2;
60     }
61
62     silc_buffer_pull(src, ret);
63   }
64
65   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
66
67   return ret;
68 }
69
70 /* QoS read handler, this will call the read and write events to indicate
71    that data is available again after a timeout. */
72
73 SILC_TASK_CALLBACK(silc_socket_read_qos)
74 {
75   SilcSocketConnection sock = context;
76   sock->qos->applied = TRUE;
77   if (sock->users > 1)
78     silc_schedule_set_listen_fd(sock->qos->schedule, sock->sock,
79                                 (SILC_TASK_READ | SILC_TASK_WRITE), TRUE);
80   sock->qos->applied = FALSE;
81   silc_socket_free(sock);
82 }
83
84 /* Reads data from the socket connection into the incoming data buffer.
85    It reads as much as possible from the socket connection. This returns
86    amount of bytes read or -1 on error or -2 on case where all of the
87    data could not be read at once. */
88
89 int silc_socket_read(SilcSocketConnection sock)
90 {
91   int len = 0;
92   unsigned char buf[SILC_SOCKET_READ_SIZE];
93   int fd = sock->sock;
94
95   if (SILC_IS_DISABLED(sock))
96     return -1;
97
98   /* If QoS was applied to socket then return earlier read data but apply
99      QoS to it too, if necessary. */
100   if (sock->qos) {
101     if (sock->qos->applied) {
102       if (sock->qos->data_len) {
103         /* Pull hidden data since we have it from earlier QoS apply */
104         silc_buffer_pull_tail(sock->inbuf, sock->qos->data_len);
105         len = sock->qos->data_len;
106         sock->qos->data_len = 0;
107       }
108
109       if (sock->inbuf->len - len > sock->qos->read_limit_bytes) {
110         /* Seems we need to apply QoS for the remaining data as well */
111         silc_schedule_task_add(sock->qos->schedule, sock->sock,
112                                silc_socket_read_qos, silc_socket_dup(sock),
113                                sock->qos->limit_sec, sock->qos->limit_usec,
114                                SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
115         silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
116       
117         /* Hide the rest of the data from the buffer. */
118         sock->qos->data_len = (sock->inbuf->len - len - 
119                                sock->qos->read_limit_bytes);
120         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
121       }
122
123       if (sock->inbuf->len)
124         return sock->inbuf->len;
125     }
126
127     /* If we were called and we have active QoS data pending, return
128        with no data */
129     if (sock->qos->data_len) {
130       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
131       return -2;
132     }
133   }
134
135   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
136
137   /* Read the data from the socket. */
138   len = read(fd, buf, sizeof(buf));
139   if (len < 0) {
140     if (errno == EAGAIN || errno == EINTR) {
141       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
142       return -2;
143     }
144     SILC_LOG_DEBUG(("Cannot read from socket: %d:%s", fd, strerror(errno)));
145     sock->sock_error = errno;
146     return -1;
147   }
148
149   if (!len)
150     return 0;
151
152   /* Insert the data to the buffer. */
153
154   if (!sock->inbuf)
155     sock->inbuf = silc_buffer_alloc(SILC_SOCKET_BUF_SIZE);
156   
157   /* If the data does not fit to the buffer reallocate it */
158   if ((sock->inbuf->end - sock->inbuf->tail) < len)
159     sock->inbuf = silc_buffer_realloc(sock->inbuf, sock->inbuf->truelen + 
160                                       (len * 2));
161   silc_buffer_put_tail(sock->inbuf, buf, len);
162   silc_buffer_pull_tail(sock->inbuf, len);
163
164   SILC_LOG_DEBUG(("Read %d bytes", len));
165
166   /* Apply QoS to the read data if necessary */
167   if (sock->qos) {
168     struct timeval curtime;
169     silc_gettimeofday(&curtime);
170
171     /* If we have passed the rate time limit, set our new time limit,
172        and zero the rate limit. */
173     if (!silc_compare_timeval(&curtime, &sock->qos->next_limit)) {
174       curtime.tv_sec++;
175       sock->qos->next_limit = curtime;
176       sock->qos->cur_rate = 0;
177     }
178     sock->qos->cur_rate++;
179
180     /* If we are not withing rate limit apply QoS for the read data */
181     if (sock->qos->cur_rate > sock->qos->read_rate) {
182       silc_schedule_task_add(sock->qos->schedule, sock->sock,
183                              silc_socket_read_qos, silc_socket_dup(sock),
184                              sock->qos->limit_sec, sock->qos->limit_usec,
185                              SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
186       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
187
188       /* Check the byte limit as well, and do not return more than allowed */
189       if (sock->inbuf->len > sock->qos->read_limit_bytes) {
190         /* Hide the rest of the data from the buffer. */
191         sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
192         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
193         len = sock->inbuf->len;
194       } else {
195         /* Rate limit kicked in, do not return data yet */
196         return -2;
197       }
198     } else {
199       /* Check the byte limit, and do not return more than allowed */
200       if (sock->inbuf->len > sock->qos->read_limit_bytes) {
201         silc_schedule_task_add(sock->qos->schedule, sock->sock,
202                                silc_socket_read_qos, silc_socket_dup(sock),
203                                sock->qos->limit_sec, sock->qos->limit_usec,
204                                SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
205         silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
206
207         /* Hide the rest of the data from the buffer. */
208         sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
209         silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
210         len = sock->inbuf->len;
211       }
212     }
213   }
214
215   return len;
216 }
217
218 /* Returns human readable socket error message */
219
220 bool silc_socket_get_error(SilcSocketConnection sock, char *error,
221                            SilcUInt32 error_len)
222 {
223   char *err;
224
225   if (!sock->sock_error)
226     return FALSE;
227
228   err = strerror(sock->sock_error);
229   if (strlen(err) > error_len)
230     return FALSE;
231
232   memset(error, 0, error_len);
233   memcpy(error, err, strlen(err));
234   return TRUE;
235 }