1 /* A multi-threaded telnet-like server that gives a Python prompt. 2 3 Usage: pysvr [port] 4 5 For security reasons, it only accepts requests from the current host. 6 This can still be insecure, but restricts violations from people who 7 can log in on your machine. Use with caution! 8 9 */ 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <ctype.h> 15 #include <errno.h> 16 17 #include <sys/types.h> 18 #include <sys/socket.h> 19 #include <netinet/in.h> 20 21 #include <pthread.h> 22 #include <getopt.h> 23 24 /* XXX Umpfh. 25 Python.h defines a typedef destructor, which conflicts with pthread.h. 26 So Python.h must be included after pthread.h. */ 27 28 #include "Python.h" 29 30 extern int Py_VerboseFlag; 31 32 #ifndef PORT 33 #define PORT 4000 34 #endif 35 36 struct workorder { 37 int conn; 38 struct sockaddr_in addr; 39 }; 40 41 /* Forward */ 42 static void init_python(void); 43 static void usage(void); 44 static void oprogname(void); 45 static void main_thread(int); 46 static void create_thread(int, struct sockaddr_in *); 47 static void *service_thread(struct workorder *); 48 static void run_interpreter(FILE *, FILE *); 49 static int run_command(char *, PyObject *); 50 static void ps(void); 51 52 static char *progname = "pysvr"; 53 54 static PyThreadState *gtstate; 55 56 main(int argc, char **argv) 57 { 58 int port = PORT; 59 int c; 60 61 if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0') 62 progname = argv[0]; 63 64 while ((c = getopt(argc, argv, "v")) != EOF) { 65 switch (c) { 66 case 'v': 67 Py_VerboseFlag++; 68 break; 69 default: 70 usage(); 71 } 72 } 73 74 if (optind < argc) { 75 if (optind+1 < argc) { 76 oprogname(); 77 fprintf(stderr, "too many arguments\n"); 78 usage(); 79 } 80 port = atoi(argv[optind]); 81 if (port <= 0) { 82 fprintf(stderr, "bad port (%s)\n", argv[optind]); 83 usage(); 84 } 85 } 86 87 main_thread(port); 88 89 fprintf(stderr, "Bye.\n"); 90 91 exit(0); 92 } 93 94 static char usage_line[] = "usage: %s [port]\n"; 95 96 static void 97 usage(void) 98 { 99 fprintf(stderr, usage_line, progname); 100 exit(2); 101 } 102 103 static void 104 main_thread(int port) 105 { 106 int sock, conn, size, i; 107 struct sockaddr_in addr, clientaddr; 108 109 sock = socket(PF_INET, SOCK_STREAM, 0); 110 if (sock < 0) { 111 oprogname(); 112 perror("can't create socket"); 113 exit(1); 114 } 115 116 #ifdef SO_REUSEADDR 117 i = 1; 118 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i); 119 #endif 120 121 memset((char *)&addr, '\0', sizeof addr); 122 addr.sin_family = AF_INET; 123 addr.sin_port = htons(port); 124 addr.sin_addr.s_addr = 0L; 125 if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) { 126 oprogname(); 127 perror("can't bind socket to address"); 128 exit(1); 129 } 130 131 if (listen(sock, 5) < 0) { 132 oprogname(); 133 perror("can't listen on socket"); 134 exit(1); 135 } 136 137 fprintf(stderr, "Listening on port %d...\n", port); 138 139 for (i = 0; ; i++) { 140 size = sizeof clientaddr; 141 memset((char *) &clientaddr, '\0', size); 142 conn = accept(sock, (struct sockaddr *) &clientaddr, &size); 143 if (conn < 0) { 144 oprogname(); 145 perror("can't accept connection from socket"); 146 exit(1); 147 } 148 149 size = sizeof addr; 150 memset((char *) &addr, '\0', size); 151 if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) { 152 oprogname(); 153 perror("can't get socket name of connection"); 154 exit(1); 155 } 156 if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) { 157 oprogname(); 158 perror("connection from non-local host refused"); 159 fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n", 160 ntohl(addr.sin_addr.s_addr), 161 ntohl(clientaddr.sin_addr.s_addr)); 162 close(conn); 163 continue; 164 } 165 if (i == 4) { 166 close(conn); 167 break; 168 } 169 create_thread(conn, &clientaddr); 170 } 171 172 close(sock); 173 174 if (gtstate) { 175 PyEval_AcquireThread(gtstate); 176 gtstate = NULL; 177 Py_Finalize(); 178 /* And a second time, just because we can. */ 179 Py_Finalize(); /* This should be harmless. */ 180 } 181 exit(0); 182 } 183 184 static void 185 create_thread(int conn, struct sockaddr_in *addr) 186 { 187 struct workorder *work; 188 pthread_t tdata; 189 190 work = malloc(sizeof(struct workorder)); 191 if (work == NULL) { 192 oprogname(); 193 fprintf(stderr, "out of memory for thread.\n"); 194 close(conn); 195 return; 196 } 197 work->conn = conn; 198 work->addr = *addr; 199 200 init_python(); 201 202 if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) { 203 oprogname(); 204 perror("can't create new thread"); 205 close(conn); 206 return; 207 } 208 209 if (pthread_detach(tdata) < 0) { 210 oprogname(); 211 perror("can't detach from thread"); 212 } 213 } 214 215 static PyThreadState *the_tstate; 216 static PyInterpreterState *the_interp; 217 static PyObject *the_builtins; 218 219 static void 220 init_python(void) 221 { 222 if (gtstate) 223 return; 224 Py_Initialize(); /* Initialize the interpreter */ 225 PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ 226 gtstate = PyEval_SaveThread(); /* Release the thread state */ 227 } 228 229 static void * 230 service_thread(struct workorder *work) 231 { 232 FILE *input, *output; 233 234 fprintf(stderr, "Start thread for connection %d.\n", work->conn); 235 236 ps(); 237 238 input = fdopen(work->conn, "r"); 239 if (input == NULL) { 240 oprogname(); 241 perror("can't create input stream"); 242 goto done; 243 } 244 245 output = fdopen(work->conn, "w"); 246 if (output == NULL) { 247 oprogname(); 248 perror("can't create output stream"); 249 fclose(input); 250 goto done; 251 } 252 253 setvbuf(input, NULL, _IONBF, 0); 254 setvbuf(output, NULL, _IONBF, 0); 255 256 run_interpreter(input, output); 257 258 fclose(input); 259 fclose(output); 260 261 done: 262 fprintf(stderr, "End thread for connection %d.\n", work->conn); 263 close(work->conn); 264 free(work); 265 } 266 267 static void 268 oprogname(void) 269 { 270 int save = errno; 271 fprintf(stderr, "%s: ", progname); 272 errno = save; 273 } 274 275 static void 276 run_interpreter(FILE *input, FILE *output) 277 { 278 PyThreadState *tstate; 279 PyObject *new_stdin, *new_stdout; 280 PyObject *mainmod, *globals; 281 char buffer[1000]; 282 char *p, *q; 283 int n, end; 284 285 PyEval_AcquireLock(); 286 tstate = Py_NewInterpreter(); 287 if (tstate == NULL) { 288 fprintf(output, "Sorry -- can't create an interpreter\n"); 289 return; 290 } 291 292 mainmod = PyImport_AddModule("__main__"); 293 globals = PyModule_GetDict(mainmod); 294 Py_INCREF(globals); 295 296 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL); 297 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL); 298 299 PySys_SetObject("stdin", new_stdin); 300 PySys_SetObject("stdout", new_stdout); 301 PySys_SetObject("stderr", new_stdout); 302 303 for (n = 1; !PyErr_Occurred(); n++) { 304 Py_BEGIN_ALLOW_THREADS 305 fprintf(output, "%d> ", n); 306 p = fgets(buffer, sizeof buffer, input); 307 Py_END_ALLOW_THREADS 308 309 if (p == NULL) 310 break; 311 if (p[0] == '\377' && p[1] == '\354') 312 break; 313 314 q = strrchr(p, '\r'); 315 if (q && q[1] == '\n' && q[2] == '\0') { 316 *q++ = '\n'; 317 *q++ = '\0'; 318 } 319 320 while (*p && isspace(*p)) 321 p++; 322 if (p[0] == '#' || p[0] == '\0') 323 continue; 324 325 end = run_command(buffer, globals); 326 if (end < 0) 327 PyErr_Print(); 328 329 if (end) 330 break; 331 } 332 333 Py_XDECREF(globals); 334 Py_XDECREF(new_stdin); 335 Py_XDECREF(new_stdout); 336 337 Py_EndInterpreter(tstate); 338 PyEval_ReleaseLock(); 339 340 fprintf(output, "Goodbye!\n"); 341 } 342 343 static int 344 run_command(char *buffer, PyObject *globals) 345 { 346 PyObject *m, *d, *v; 347 fprintf(stderr, "run_command: %s", buffer); 348 if (strchr(buffer, '\n') == NULL) 349 fprintf(stderr, "\n"); 350 v = PyRun_String(buffer, Py_single_input, globals, globals); 351 if (v == NULL) { 352 if (PyErr_Occurred() == PyExc_SystemExit) { 353 PyErr_Clear(); 354 return 1; 355 } 356 PyErr_Print(); 357 return 0; 358 } 359 Py_DECREF(v); 360 return 0; 361 } 362 363 static void 364 ps(void) 365 { 366 char buffer[100]; 367 PyOS_snprintf(buffer, sizeof(buffer), 368 "ps -l -p %d </dev/null | sed 1d\n", getpid()); 369 system(buffer); 370 } 371