]> andersk Git - test.git/blob - shellinabox/session.c
Some changes to improve compatibility with MacOS X.
[test.git] / shellinabox / session.c
1 // session.c -- Session management for HTTP/HTTPS connections
2 // Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License version 2 as
6 // published by the Free Software Foundation.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License along
14 // with this program; if not, write to the Free Software Foundation, Inc.,
15 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 //
17 // In addition to these license terms, the author grants the following
18 // additional rights:
19 //
20 // If you modify this program, or any covered work, by linking or
21 // combining it with the OpenSSL project's OpenSSL library (or a
22 // modified version of that library), containing parts covered by the
23 // terms of the OpenSSL or SSLeay licenses, the author
24 // grants you additional permission to convey the resulting work.
25 // Corresponding Source for a non-source form of such a combination
26 // shall include the source code for the parts of OpenSSL used as well
27 // as that of the covered work.
28 //
29 // You may at your option choose to remove this additional permission from
30 // the work, or from any part of it.
31 //
32 // It is possible to build this program in a way that it loads OpenSSL
33 // libraries at run-time. If doing so, the following notices are required
34 // by the OpenSSL and SSLeay licenses:
35 //
36 // This product includes software developed by the OpenSSL Project
37 // for use in the OpenSSL Toolkit. (http://www.openssl.org/)
38 //
39 // This product includes cryptographic software written by Eric Young
40 // (eay@cryptsoft.com)
41 //
42 //
43 // The most up-to-date version of this program is always available from
44 // http://shellinabox.com
45
46 #include "config.h"
47
48 #include <stdlib.h>
49 #include <string.h>
50 #include <time.h>
51 #include <fcntl.h>
52 #include <unistd.h>
53
54 #include "shellinabox/session.h"
55 #include "logging/logging.h"
56
57 #ifdef HAVE_UNUSED
58 #defined ATTR_UNUSED __attribute__((unused))
59 #defined UNUSED(x)   do { } while (0)
60 #else
61 #define ATTR_UNUSED
62 #define UNUSED(x)    do { (void)(x); } while (0)
63 #endif
64
65 static HashMap *sessions;
66
67
68 static struct Graveyard {
69   struct Graveyard *next;
70   time_t           timeout;
71   const char       *sessionKey;
72 } *graveyard;
73
74 void addToGraveyard(struct Session *session) {
75   // It is possible for a child process to die, but for the Session to
76   // linger around, because the browser has also navigated away and thus
77   // nobody ever calls completePendingRequest(). We put these Sessions into
78   // the graveyard and reap them after a while.
79   struct Graveyard *g;
80   check(g       = malloc(sizeof(struct Graveyard)));
81   g->next       = graveyard;
82   g->timeout    = time(NULL) + AJAX_TIMEOUT;
83   g->sessionKey = strdup(session->sessionKey);
84   graveyard     = g;
85 }
86
87 static void checkGraveyardInternal(int expireAll) {
88   if (!graveyard) {
89     return;
90   }
91   time_t timeout = time(NULL) - (expireAll ? 2*AJAX_TIMEOUT : 0);
92   for (struct Graveyard **g = &graveyard, *old = *g;
93        old; ) {
94     if (old->timeout < timeout) {
95       *g         = old->next;
96       deleteFromHashMap(sessions, old->sessionKey);
97       free((char *)old->sessionKey);
98       free(old);
99     } else {
100       g          = &old->next;
101     }
102     old          = *g;
103   }
104 }
105
106 void checkGraveyard(void) {
107   checkGraveyardInternal(0);
108 }
109
110 void initSession(struct Session *session, const char *sessionKey,
111                  Server *server, URL *url, const char *peerName) {
112   session->sessionKey     = sessionKey;
113   session->server         = server;
114   check(session->peerName = strdup(peerName));
115   session->connection     = NULL;
116   session->http           = NULL;
117   session->url            = url;
118   session->done           = 0;
119   session->pty            = -1;
120   session->width          = 0;
121   session->height         = 0;
122   session->buffered       = NULL;
123   session->len            = 0;
124 }
125
126 struct Session *newSession(const char *sessionKey, Server *server, URL *url,
127                            const char *peerName) {
128   struct Session *session;
129   check(session = malloc(sizeof(struct Session)));
130   initSession(session, sessionKey, server, url, peerName);
131   return session;
132 }
133
134 void destroySession(struct Session *session) {
135   if (session) {
136     free((char *)session->peerName);
137     free((char *)session->sessionKey);
138     deleteURL(session->url);
139     if (session->pty >= 0) {
140       NOINTR(close(session->pty));
141     }
142   }
143 }
144
145 void deleteSession(struct Session *session) {
146   destroySession(session);
147   free(session);
148 }
149
150 void abandonSession(struct Session *session) {
151   deleteFromHashMap(sessions, session->sessionKey);
152 }
153
154 void finishSession(struct Session *session) {
155   deleteFromHashMap(sessions, session->sessionKey);
156 }
157
158 void finishAllSessions(void) {
159   checkGraveyardInternal(1);
160   deleteHashMap(sessions);
161 }
162
163 static void destroySessionHashEntry(void *arg ATTR_UNUSED,
164                                     char *key ATTR_UNUSED, char *value) {
165   UNUSED(arg);
166   UNUSED(key);
167
168   deleteSession((struct Session *)value);
169 }
170
171 char *newSessionKey(void) {
172   int fd;
173   check((fd = NOINTR(open("/dev/urandom", O_RDONLY))) >= 0);
174   unsigned char buf[16];
175   check(NOINTR(read(fd, buf, sizeof(buf))) == sizeof(buf));
176   NOINTR(close(fd));
177   char *sessionKey;
178   check(sessionKey   = malloc((8*sizeof(buf) + 5)/6 + 1));
179   char *ptr          = sessionKey;
180   int count          = 0;
181   int bits           = 0;
182   for (unsigned i = 0;;) {
183     bits             = (bits << 8) | buf[i];
184     count           += 8;
185   drain:
186     while (count >= 6) {
187       *ptr++         = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
188                        "ghijklmnopqrstuvwxyz0123456789-/"
189                        [(bits >> (count -= 6)) & 0x3F];
190     }
191     if (++i >= sizeof(buf)) {
192       if (count && i == sizeof(buf)) {
193         bits       <<= 8;
194         count       += 8;
195         goto drain;
196       } else {
197         break;
198       }
199     }
200   }
201   *ptr               = '\000';
202   check(!sessions || !getFromHashMap(sessions, sessionKey));
203   return sessionKey;
204 }
205
206 struct Session *findCGISession(int *isNew, HttpConnection *http, URL *url,
207                                const char *cgiSessionKey) {
208   *isNew                 = 1;
209   if (!sessions) {
210     sessions             = newHashMap(destroySessionHashEntry, NULL);
211   }
212   const HashMap *args    = urlGetArgs(url);
213   const char *sessionKey = getFromHashMap(args, "session");
214   struct Session *session= NULL;
215   if (cgiSessionKey &&
216       (!sessionKey || strcmp(cgiSessionKey, sessionKey))) {
217     // In CGI mode, we only ever allow exactly one session with a
218     // pre-negotiated key.
219     deleteURL(url);
220   } else {
221     if (sessionKey && *sessionKey) {
222       session            = (struct Session *)getFromHashMap(sessions,
223                                                             sessionKey);
224     }
225     if (session) {
226       *isNew             = 0;
227       deleteURL(session->url);
228       session->url       = url;
229     } else if (!cgiSessionKey && sessionKey && *sessionKey) {
230       *isNew             = 0;
231       debug("Failed to find session: %s", sessionKey);
232       deleteURL(url);
233     } else {
234       // First contact. Create session, now.
235       check(sessionKey   = cgiSessionKey ? strdup(cgiSessionKey)
236                                          : newSessionKey());
237       session            = newSession(sessionKey, httpGetServer(http), url,
238                                       httpGetPeerName(http));
239       addToHashMap(sessions, sessionKey, (const char *)session);
240       debug("Creating a new session: %s", sessionKey);
241     }
242   }
243   return session;
244 }
245
246 struct Session *findSession(int *isNew, HttpConnection *http, URL *url) {
247   return findCGISession(isNew, http, url, NULL);
248 }
249
250 void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg){
251   iterateOverHashMap(sessions, fnc, arg);
252 }
253
254 int numSessions(void) {
255   return getHashmapSize(sessions);
256 }
This page took 0.139144 seconds and 5 git commands to generate.