nixio: Finetuning of TLS-support
[project/luci.git] / libs / 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 == 1) {
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 static int nixio_tls_sock_recv(lua_State *L) {
49         SSL *sock = nixio__checktlssock(L);
50         int req = luaL_checkinteger(L, 2);
51
52         luaL_argcheck(L, req >= 0, 2, "out of range");
53
54         /* We limit the readsize to NIXIO_BUFFERSIZE */
55         req = (req > NIXIO_BUFFERSIZE) ? NIXIO_BUFFERSIZE : req;
56
57 #ifndef WITH_AXTLS
58
59         char buffer[NIXIO_BUFFERSIZE];
60         int readc = SSL_read(sock, buffer, req);
61
62         if (readc < 0) {
63                 return nixio__tls_sock_pstatus(L, sock, readc);
64         } else {
65                 lua_pushlstring(L, buffer, readc);
66                 return 1;
67         }
68
69 #else
70
71         if (!req) {
72                 lua_pushliteral(L, "");
73                 return 1;
74         }
75
76         nixio_tls_sock *t = lua_touserdata(L, 1);
77
78         /* AXTLS doesn't handle buffering for us, so we have to hack around*/
79         if (req < t->pbufsiz) {
80                 lua_pushlstring(L, t->pbufpos, req);
81                 t->pbufpos += req;
82                 t->pbufsiz -= req;
83                 return 1;
84         } else {
85                 char *axbuf;
86                 int axread;
87
88                 /* while handshake pending */
89                 while ((axread = ssl_read(sock, (uint8_t**)&axbuf)) == SSL_OK);
90
91                 if (t->pbufsiz) {
92                         lua_pushlstring(L, t->pbufpos, t->pbufsiz);
93                 }
94
95                 if (axread < 0) {
96                         /* There is an error */
97                         free(t->pbuffer);
98                         t->pbuffer = t->pbufpos = NULL;
99                         t->pbufsiz = 0;
100
101                         if (axread != SSL_ERROR_CONN_LOST) {
102                                 return nixio__tls_sock_perror(L, sock, axread);
103                         } else {
104                                 if (!t->pbufsiz) {
105                                         lua_pushliteral(L, "");
106                                 }
107                         }
108                 } else {
109                         int stillwant = req - t->pbufsiz;
110                         if (stillwant < axread) {
111                                 /* we got more data than we need */
112                                 lua_pushlstring(L, axbuf, stillwant);
113                                 if(t->pbufsiz) {
114                                         lua_concat(L, 2);
115                                 }
116
117                                 /* remaining data goes into the buffer */
118                                 t->pbufpos = t->pbuffer;
119                                 t->pbufsiz = axread - stillwant;
120                                 t->pbuffer = realloc(t->pbuffer, t->pbufsiz);
121                                 if (!t->pbuffer) {
122                                         free(t->pbufpos);
123                                         t->pbufpos = NULL;
124                                         t->pbufsiz = 0;
125                                         return luaL_error(L, "out of memory");
126                                 }
127
128                                 t->pbufpos = t->pbuffer;
129                                 memcpy(t->pbufpos, axbuf + stillwant, t->pbufsiz);
130                         } else {
131                                 lua_pushlstring(L, axbuf, axread);
132                                 if(t->pbufsiz) {
133                                         lua_concat(L, 2);
134                                 }
135
136                                 /* free buffer */
137                                 free(t->pbuffer);
138                                 t->pbuffer = t->pbufpos = NULL;
139                                 t->pbufsiz = 0;
140                         }
141                 }
142                 return 1;
143         }
144
145 #endif /* WITH_AXTLS */
146
147 }
148
149 static int nixio_tls_sock_send(lua_State *L) {
150         SSL *sock = nixio__checktlssock(L);
151         size_t len;
152         ssize_t sent;
153         const char *data = luaL_checklstring(L, 2, &len);
154         sent = SSL_write(sock, data, len);
155         if (sent > 0) {
156                 lua_pushinteger(L, sent);
157                 return 1;
158         } else {
159                 return nixio__tls_sock_pstatus(L, sock, len);
160         }
161 }
162
163 static int nixio_tls_sock_accept(lua_State *L) {
164         SSL *sock = nixio__checktlssock(L);
165         return nixio__tls_sock_pstatus(L, sock, SSL_accept(sock));
166 }
167
168 static int nixio_tls_sock_connect(lua_State *L) {
169         SSL *sock = nixio__checktlssock(L);
170         return nixio__tls_sock_pstatus(L, sock, SSL_connect(sock));
171 }
172
173 static int nixio_tls_sock_shutdown(lua_State *L) {
174         SSL *sock = nixio__checktlssock(L);
175         return nixio__tls_sock_pstatus(L, sock, SSL_shutdown(sock));
176 }
177
178 static int nixio_tls_sock__gc(lua_State *L) {
179         nixio_tls_sock *sock = luaL_checkudata(L, 1, NIXIO_TLS_SOCK_META);
180         if (sock->socket) {
181                 SSL_free(sock->socket);
182                 sock->socket = NULL;
183 #ifdef WITH_AXTLS
184                 free(sock->pbuffer);
185 #endif
186         }
187         return 0;
188 }
189
190 static int nixio_tls_sock__tostring(lua_State *L) {
191         SSL *sock = nixio__checktlssock(L);
192         lua_pushfstring(L, "nixio TLS connection: %p", sock);
193         return 1;
194 }
195
196
197 /* ctx function table */
198 static const luaL_reg M[] = {
199         {"recv",                nixio_tls_sock_recv},
200         {"send",                nixio_tls_sock_send},
201         {"accept",              nixio_tls_sock_accept},
202         {"connect",     nixio_tls_sock_connect},
203         {"shutdown",    nixio_tls_sock_shutdown},
204         {"__gc",                nixio_tls_sock__gc},
205         {"__tostring",  nixio_tls_sock__tostring},
206         {NULL,                  NULL}
207 };
208
209
210 void nixio_open_tls_socket(lua_State *L) {
211         /* create socket metatable */
212         luaL_newmetatable(L, NIXIO_TLS_SOCK_META);
213         luaL_register(L, NULL, M);
214         lua_pushvalue(L, -1);
215         lua_setfield(L, -2, "__index");
216         lua_setfield(L, -2, "tls_socket_meta");
217 }