Rework LuCI build system
[project/luci.git] / libs / luci-lib-nixio / src / tls-socket.c
1  /*
2  * nixio - Linux I/O library for lua
3  *
4  *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "nixio-tls.h"
20 #include <string.h>
21 #include <stdlib.h>
22
23 static int nixio__tls_sock_perror(lua_State *L, SSL *sock, int code) {
24         lua_pushnil(L);
25         lua_pushinteger(L, SSL_get_error(sock, code));
26         return 2;
27 }
28
29 static int nixio__tls_sock_pstatus(lua_State *L, SSL *sock, int code) {
30         if (code > 0) {
31                 lua_pushboolean(L, 1);
32                 return 1;
33         } else {
34                 return nixio__tls_sock_perror(L, sock, code);
35         }
36 }
37
38 static SSL* nixio__checktlssock(lua_State *L) {
39         if (lua_istable(L, 1)) {
40                 lua_getfield(L, 1, "connection");
41                 lua_replace(L, 1);
42         }
43         nixio_tls_sock *sock = luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META);
44         luaL_argcheck(L, sock->socket, 1, "invalid context");
45         return sock->socket;
46 }
47
48 #ifndef WITH_AXTLS
49 #define nixio_tls__check_connected(L) ;
50
51 #define nixio_tls__set_connected(L, val) ;
52 #else
53 #define nixio_tls__check_connected(L) \
54         nixio_tls_sock *ctsock = luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META);    \
55         if (!ctsock->connected) {       \
56                 lua_pushnil(L);                 \
57                 lua_pushinteger(L, 1);  \
58                 return 2;                               \
59         }
60
61 #define nixio_tls__set_connected(L, val) \
62 ((nixio_tls_sock*)luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META))->connected = val;
63 #endif /* WITH_AXTLS */
64
65 static int nixio_tls_sock_recv(lua_State *L) {
66         SSL *sock = nixio__checktlssock(L);
67         nixio_tls__check_connected(L);
68         uint req = luaL_checkinteger(L, 2);
69
70         luaL_argcheck(L, req >= 0, 2, "out of range");
71
72         /* We limit the readsize to NIXIO_BUFFERSIZE */
73         req = (req > NIXIO_BUFFERSIZE) ? NIXIO_BUFFERSIZE : req;
74
75 #ifndef WITH_AXTLS
76
77         char buffer[NIXIO_BUFFERSIZE];
78         int readc = SSL_read(sock, buffer, req);
79
80         if (readc < 0) {
81                 return nixio__tls_sock_pstatus(L, sock, readc);
82         } else {
83                 lua_pushlstring(L, buffer, readc);
84                 return 1;
85         }
86
87 #else
88
89         if (!req) {
90                 lua_pushliteral(L, "");
91                 return 1;
92         }
93
94         nixio_tls_sock *t = lua_touserdata(L, 1);
95
96         /* AXTLS doesn't handle buffering for us, so we have to hack around*/
97         if (req < t->pbufsiz) {
98                 lua_pushlstring(L, t->pbufpos, req);
99                 t->pbufpos += req;
100                 t->pbufsiz -= req;
101                 return 1;
102         } else {
103                 uint8_t *axbuf;
104                 int axread;
105
106                 /* while handshake pending */
107                 while ((axread = ssl_read(sock, &axbuf)) == SSL_OK);
108
109                 if (t->pbufsiz) {
110                         lua_pushlstring(L, t->pbufpos, t->pbufsiz);
111                 }
112
113                 if (axread < 0) {
114                         /* There is an error */
115                         free(t->pbuffer);
116                         t->pbuffer = t->pbufpos = NULL;
117
118                         if (axread != SSL_ERROR_CONN_LOST) {
119                                 t->pbufsiz = 0;
120                                 return nixio__tls_sock_perror(L, sock, axread);
121                         } else {
122                                 if (!t->pbufsiz) {
123                                         lua_pushliteral(L, "");
124                                 } else {
125                                         t->pbufsiz = 0;
126                                 }
127                         }
128                 } else {
129                         int stillwant = req - t->pbufsiz;
130                         if (stillwant < axread) {
131                                 /* we got more data than we need */
132                                 lua_pushlstring(L, (char *)axbuf, stillwant);
133                                 if(t->pbufsiz) {
134                                         lua_concat(L, 2);
135                                 }
136
137                                 /* remaining data goes into the buffer */
138                                 t->pbufpos = t->pbuffer;
139                                 t->pbufsiz = axread - stillwant;
140                                 t->pbuffer = realloc(t->pbuffer, t->pbufsiz);
141                                 if (!t->pbuffer) {
142                                         free(t->pbufpos);
143                                         t->pbufpos = NULL;
144                                         t->pbufsiz = 0;
145                                         return luaL_error(L, "out of memory");
146                                 }
147
148                                 t->pbufpos = t->pbuffer;
149                                 memcpy(t->pbufpos, axbuf + stillwant, t->pbufsiz);
150                         } else {
151                                 lua_pushlstring(L, (char *)axbuf, axread);
152                                 if(t->pbufsiz) {
153                                         lua_concat(L, 2);
154                                 }
155
156                                 /* free buffer */
157                                 free(t->pbuffer);
158                                 t->pbuffer = t->pbufpos = NULL;
159                                 t->pbufsiz = 0;
160                         }
161                 }
162                 return 1;
163         }
164
165 #endif /* WITH_AXTLS */
166
167 }
168
169 static int nixio_tls_sock_send(lua_State *L) {
170         SSL *sock = nixio__checktlssock(L);
171         nixio_tls__check_connected(L);
172         size_t len;
173         ssize_t sent;
174         const char *data = luaL_checklstring(L, 2, &len);
175
176         if (lua_gettop(L) > 2) {
177                 int offset = luaL_optint(L, 3, 0);
178                 if (offset) {
179                         if (offset < len) {
180                                 data += offset;
181                                 len -= offset;
182                         } else {
183                                 len = 0;
184                         }
185                 }
186
187                 unsigned int wlen = luaL_optint(L, 4, len);
188                 if (wlen < len) {
189                         len = wlen;
190                 }
191         }
192
193         sent = SSL_write(sock, data, len);
194         if (sent > 0) {
195                 lua_pushinteger(L, sent);
196                 return 1;
197         } else {
198                 return nixio__tls_sock_pstatus(L, sock, sent);
199         }
200 }
201
202 static int nixio_tls_sock_accept(lua_State *L) {
203         SSL *sock = nixio__checktlssock(L);
204         const int stat = SSL_accept(sock);
205         nixio_tls__set_connected(L, stat == 1);
206         return nixio__tls_sock_pstatus(L, sock, stat);
207 }
208
209 static int nixio_tls_sock_connect(lua_State *L) {
210         SSL *sock = nixio__checktlssock(L);
211         const int stat = SSL_connect(sock);
212         nixio_tls__set_connected(L, stat == 1);
213         return nixio__tls_sock_pstatus(L, sock, stat);
214 }
215
216 static int nixio_tls_sock_shutdown(lua_State *L) {
217         SSL *sock = nixio__checktlssock(L);
218         nixio_tls__set_connected(L, 0);
219         return nixio__tls_sock_pstatus(L, sock, SSL_shutdown(sock));
220 }
221
222 static int nixio_tls_sock__gc(lua_State *L) {
223         nixio_tls_sock *sock = luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META);
224         if (sock->socket) {
225                 SSL_free(sock->socket);
226                 sock->socket = NULL;
227 #ifdef WITH_AXTLS
228                 free(sock->pbuffer);
229 #endif
230         }
231         return 0;
232 }
233
234 static int nixio_tls_sock__tostring(lua_State *L) {
235         SSL *sock = nixio__checktlssock(L);
236         lua_pushfstring(L, "nixio TLS connection: %p", sock);
237         return 1;
238 }
239
240
241 /* ctx function table */
242 static const luaL_reg M[] = {
243         {"recv",                nixio_tls_sock_recv},
244         {"send",                nixio_tls_sock_send},
245         {"read",                nixio_tls_sock_recv},
246         {"write",               nixio_tls_sock_send},
247         {"accept",              nixio_tls_sock_accept},
248         {"connect",     nixio_tls_sock_connect},
249         {"shutdown",    nixio_tls_sock_shutdown},
250         {"__gc",                nixio_tls_sock__gc},
251         {"__tostring",  nixio_tls_sock__tostring},
252         {NULL,                  NULL}
253 };
254
255
256 void nixio_open_tls_socket(lua_State *L) {
257         /* create socket metatable */
258         luaL_newmetatable(L, NIXIO_TLS_SOCK_META);
259         luaL_register(L, NULL, M);
260         lua_pushvalue(L, -1);
261         lua_setfield(L, -2, "__index");
262         lua_setfield(L, -2, "meta_tls_socket");
263 }