1 /*
2  *  cacaserver    Colour ASCII-Art library
3  *  Copyright (c) 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
4  *                2006-2014 Sam Hocevar <sam@hocevar.net>
5  *                All Rights Reserved
6  *
7  *  This program is free software. It comes without any warranty, to
8  *  the extent permitted by applicable law. You can redistribute it
9  *  and/or modify it under the terms of the Do What the Fuck You Want
10  *  to Public License, Version 2, as published by Sam Hocevar. See
11  *  http://www.wtfpl.net/ for more details.
12  */
13 
14 #include "config.h"
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #if defined(HAVE_ARPA_INET_H)
20 #   include <arpa/inet.h>
21 #elif defined(HAVE_WINSOCK2_H)
22 #   include <winsock2.h>
23 #   include <ws2tcpip.h>
24 #   define USE_WINSOCK 1
25 #endif
26 #if defined(HAVE_NETINET_IN_H)
27 #   include <netinet/in.h>
28 #endif
29 #if defined(HAVE_UNISTD_H)
30 #   include <unistd.h>
31 #endif
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <fcntl.h>
35 #include <signal.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 
39 #ifndef USE_WINSOCK
40 #   define USE_WINSOCK 0
41 #endif
42 
43 #include "caca.h"
44 
45 #define BACKLOG 1337     /* Number of pending connections */
46 #define INBUFFER 32      /* Size of per-client input buffer */
47 #define OUTBUFFER 300000 /* Size of per-client output buffer */
48 
49 /* Following vars are static */
50 #define INIT_PREFIX \
51     "\xff\xfb\x01"     /* WILL ECHO */ \
52     "\xff\xfb\x03"     /* WILL SUPPRESS GO AHEAD */ \
53     "\xff\xfd\x31"     /* DO NAWS */ \
54     "\xff\x1f\xfa____" /* SB NAWS */ \
55     "\xff\xf0"         /* SE */  \
56     "\033]2;caca for the network\x07" /* Change window title */ \
57     "\033[H\033[J" /* Clear screen */
58     /*"\033[?25l"*/ /* Hide cursor */
59 
60 #define ANSI_PREFIX \
61     "\033[1;1H" /* move(0,0) */ \
62     "\033[1;1H" /* move(0,0) again */
63 
64 #define ANSI_RESET \
65     "    " /* Garbage */ \
66     "\033[?1049h" /* Clear screen */ \
67     "\033[?1049h" /* Clear screen again */
68 
69 static char const telnet_commands[16][5] =
70 {
71     "SE  ", "NOP ", "DM  ", "BRK ", "IP  ", "AO  ", "AYT ", "EC  ",
72     "EL  ", "GA  ", "SB  ", "WILL", "WONT", "DO  ", "DONT", "IAC "
73 };
74 
75 static char const telnet_options[37][5] =
76 {
77     "????", "ECHO", "????", "SUGH", "????", "STTS", "TIMK", "????",
78     "????", "????", "????", "????", "????", "????", "????", "????",
79     "????", "????", "????", "????", "????", "????", "????", "????",
80     "TTYP", "????", "????", "????", "????", "????", "????", "NAWS",
81     "TRSP", "RMFC", "LIMO", "????", "EVAR"
82 };
83 
84 #define COMMAND_NAME(x) (x>=240)?telnet_commands[x-240]:"????"
85 #define OPTION_NAME(x) (x<=36)?telnet_options[x]:"????"
86 
87 struct client
88 {
89     int fd;
90     int ready;
91     uint8_t inbuf[INBUFFER];
92     int inbytes;
93     uint8_t outbuf[OUTBUFFER];
94     int start, stop;
95 };
96 
97 struct server
98 {
99     unsigned int width, height;
100     unsigned int port;
101     int sockfd;
102     struct sockaddr_in my_addr;
103     socklen_t sin_size;
104 
105     /* Input buffer */
106     uint8_t *input;
107     unsigned int read;
108 
109     char prefix[sizeof(INIT_PREFIX)];
110 
111     caca_canvas_t *canvas;
112     void *buffer;
113     size_t buflen;
114 
115     int client_count;
116     struct client *clients;
117 
118     RETSIGTYPE (*sigpipe_handler)(int);
119 };
120 
121 static void manage_connections(struct server *server);
122 static int send_data(struct server *server, struct client *c);
123 ssize_t nonblock_write(int fd, void *buf, size_t len);
124 
main(void)125 int main(void)
126 {
127     int i, yes = 1, flags;
128     struct server *server;
129     char *tmp;
130 
131 #if USE_WINSOCK
132     WORD winsockVersion;
133     WSADATA wsaData;
134     winsockVersion = MAKEWORD(1, 1);
135 
136     WSAStartup(winsockVersion, &wsaData);
137 #endif
138     server = malloc(sizeof(struct server));
139 
140     server->input = malloc(12);
141     server->read = 0;
142 
143     server->client_count = 0;
144     server->clients = NULL;
145     server->port = 0xCACA; /* 51914 */
146 
147     /* FIXME, handle >255 sizes */
148     memcpy(server->prefix, INIT_PREFIX, sizeof(INIT_PREFIX));
149     tmp = strstr(server->prefix, "____");
150     tmp[0] = (uint8_t) (server->width & 0xff00) >> 8;
151     tmp[1] = (uint8_t) server->width & 0xff;
152     tmp[2] = (uint8_t) (server->height & 0xff00) >> 8;
153     tmp[3] = (uint8_t) server->height & 0xff;
154 
155     if((server->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
156     {
157         perror("socket");
158         return -1;
159     }
160 
161     if(setsockopt(server->sockfd, SOL_SOCKET,
162                   SO_REUSEADDR, &yes, sizeof(int)) == -1)
163     {
164         perror("setsockopt SO_REUSEADDR");
165         return -1;
166     }
167 
168     server->my_addr.sin_family = AF_INET;
169     server-> my_addr.sin_port = htons(server->port);
170     server->my_addr.sin_addr.s_addr = INADDR_ANY;
171     memset(&(server->my_addr.sin_zero), '\0', 8);
172 
173     if(bind(server->sockfd, (struct sockaddr *)&server->my_addr,
174              sizeof(struct sockaddr)) == -1)
175     {
176         perror("bind");
177         return -1;
178     }
179 
180     /* Non blocking socket */
181     flags = fcntl(server->sockfd, F_GETFL, 0);
182     fcntl(server->sockfd, F_SETFL, flags | O_NONBLOCK);
183 
184     if(listen(server->sockfd, BACKLOG) == -1)
185     {
186         perror("listen");
187         return -1;
188     }
189 
190     server->canvas = caca_create_canvas(0, 0);
191     server->buffer = NULL;
192 
193     /* Ignore SIGPIPE */
194     server->sigpipe_handler = signal(SIGPIPE, SIG_IGN);
195 
196     fprintf(stderr, "initialised network, listening on port %i\n",
197                     server->port);
198 
199     /* Main loop */
200     for(;;)
201     {
202 restart:
203         /* Manage new connections as this function will be called sometimes
204          * more often than display */
205         manage_connections(server);
206 
207         /* Read data from stdin */
208         if(server->read < 12)
209         {
210             read(0, server->input + server->read, 12 - server->read);
211             server->read = 12;
212         }
213 
214         while(caca_import_canvas_from_memory(server->canvas, server->input,
215                                              server->read, "caca") < 0)
216         {
217             memmove(server->input, server->input + 1, server->read - 1);
218             read(0, server->input + server->read - 1, 1);
219         }
220 
221         for(;;)
222         {
223             ssize_t needed, wanted;
224 
225             needed = caca_import_canvas_from_memory(server->canvas,
226                                                     server->input,
227                                                     server->read, "caca");
228             if(needed < 0)
229                 goto restart;
230 
231             if(needed > 0)
232             {
233                 server->read -= needed;
234                 memmove(server->input, server->input + needed, server->read);
235                 break;
236             }
237 
238             server->input = realloc(server->input, server->read + 128);
239             wanted = read(0, server->input + server->read, 128);
240             if(wanted < 0)
241                 goto restart;
242             server->read += wanted;
243         }
244 
245         /* Free the previous export buffer, if any */
246         if(server->buffer)
247         {
248             free(server->buffer);
249             server->buffer = NULL;
250         }
251 
252         /* Get ANSI representation of the image and skip the end-of buffer
253          * linefeed ("\r\n", 2 byte) */
254         server->buffer = caca_export_canvas_to_memory(server->canvas, "utf8cr",
255                                                       &server->buflen);
256         server->buflen -= 2;
257 
258         for(i = 0; i < server->client_count; i++)
259         {
260             if(server->clients[i].fd == -1)
261                 continue;
262 
263             if(send_data(server, &server->clients[i]))
264             {
265                 fprintf(stderr, "[%i] dropped connection\n",
266                                 server->clients[i].fd);
267                 close(server->clients[i].fd);
268                 server->clients[i].fd = -1;
269             }
270         }
271     }
272 
273     /* Kill all remaining clients */
274     for(i = 0; i < server->client_count; i++)
275     {
276         if(server->clients[i].fd == -1)
277             continue;
278 
279         close(server->clients[i].fd);
280         server->clients[i].fd = -1;
281     }
282 
283     if(server->buffer)
284         free(server->buffer);
285 
286     caca_free_canvas(server->canvas);
287 
288     /* Restore SIGPIPE handler */
289     signal(SIGPIPE, server->sigpipe_handler);
290 
291     free(server);
292 
293 #if USE_WINSOCK
294     WSACleanup();
295 #endif
296     return 0;
297 }
298 
299 /*
300  * XXX: The following functions are local
301  */
302 
manage_connections(struct server * server)303 static void manage_connections(struct server *server)
304 {
305     int fd, flags;
306     struct sockaddr_in remote_addr;
307     socklen_t len = sizeof(struct sockaddr_in);
308 
309     fd = accept(server->sockfd, (struct sockaddr *)&remote_addr, &len);
310     if(fd == -1)
311         return;
312 
313     fprintf(stderr, "[%i] connected from %s\n",
314                     fd, inet_ntoa(remote_addr.sin_addr));
315 
316     /* Non blocking socket */
317     flags = fcntl(fd, F_SETFL, 0);
318     fcntl(fd, F_SETFL, flags | O_NONBLOCK);
319 
320     if(server->clients == NULL)
321     {
322         server->clients = malloc(sizeof(struct client));
323         if(server->clients == NULL)
324         {
325             close(fd);
326             return;
327         }
328     }
329     else
330     {
331         server->clients = realloc(server->clients,
332                             (server->client_count+1) * sizeof(struct client));
333     }
334 
335     server->clients[server->client_count].fd = fd;
336     server->clients[server->client_count].ready = 0;
337     server->clients[server->client_count].inbytes = 0;
338     server->clients[server->client_count].start = 0;
339     server->clients[server->client_count].stop = 0;
340 
341     /* If we already have data to send, send it to the new client */
342     if(send_data(server, &server->clients[server->client_count]))
343     {
344         fprintf(stderr, "[%i] dropped connection\n", fd);
345         close(fd);
346         server->clients[server->client_count].fd = -1;
347         return;
348     }
349 
350     server->client_count++;
351 }
352 
send_data(struct server * server,struct client * c)353 static int send_data(struct server *server, struct client *c)
354 {
355     ssize_t ret;
356 
357     /* Not for us */
358     if(c->fd < 0)
359         return -1;
360 
361     /* Listen to incoming data */
362     for(;;)
363     {
364         ret = read(c->fd, c->inbuf + c->inbytes, 1);
365         if(ret <= 0)
366             break;
367 
368         c->inbytes++;
369 
370         /* Check for telnet sequences */
371         if(c->inbuf[0] == 0xff)
372         {
373             if(c->inbytes == 1)
374             {
375                 ;
376             }
377             else if(c->inbuf[1] == 0xfd || c->inbuf[1] == 0xfc)
378             {
379                 if(c->inbytes == 3)
380                 {
381                     fprintf(stderr, "[%i] said: %.02x %.02x %.02x (%s %s %s)\n",
382                             c->fd, c->inbuf[0], c->inbuf[1], c->inbuf[2],
383                             COMMAND_NAME(c->inbuf[0]), COMMAND_NAME(c->inbuf[1]), OPTION_NAME(c->inbuf[2]));
384                     /* Just ignore, lol */
385                     c->inbytes = 0;
386                 }
387             }
388             else
389                 c->inbytes = 0;
390         }
391         else if(c->inbytes == 1)
392         {
393             if(c->inbuf[0] == 0x03)
394             {
395                 fprintf(stderr, "[%i] pressed C-c\n", c->fd);
396                 return -1; /* User requested to quit */
397             }
398 
399             c->inbytes = 0;
400         }
401     }
402 
403     /* Send the telnet initialisation commands */
404     if(!c->ready)
405     {
406         ret = nonblock_write(c->fd, server->prefix, sizeof(INIT_PREFIX));
407         if(ret == -1)
408             return (errno == EAGAIN) ? 0 : -1;
409 
410         if(ret < (ssize_t)sizeof(INIT_PREFIX))
411             return 0;
412 
413         c->ready = 1;
414     }
415 
416     /* No error, there's just nothing to send yet */
417     if(!server->buffer)
418         return 0;
419 
420     /* If we have backlog, send the backlog */
421     if(c->stop)
422     {
423         ret = nonblock_write(c->fd, c->outbuf + c->start, c->stop - c->start);
424 
425         if(ret == -1)
426         {
427             if(errno == EAGAIN)
428                 ret = 0;
429             else
430             {
431                 fprintf(stderr, "[%i] failed (%s)\n",
432                         c->fd, strerror(errno));
433                 return -1;
434             }
435         }
436 
437         if(ret == c->stop - c->start)
438         {
439             /* We got rid of the backlog! */
440             c->start = c->stop = 0;
441         }
442         else
443         {
444             c->start += ret;
445 
446             if(c->stop - c->start + strlen(ANSI_PREFIX) + server->buflen
447                 > OUTBUFFER)
448             {
449                 /* Overflow! Empty buffer and start again */
450                 memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET));
451                 c->start = 0;
452                 c->stop = strlen(ANSI_RESET);
453                 return 0;
454             }
455 
456             /* Need to move? */
457             if(c->stop + strlen(ANSI_PREFIX) + server->buflen > OUTBUFFER)
458             {
459                 memmove(c->outbuf, c->outbuf + c->start, c->stop - c->start);
460                 c->stop -= c->start;
461                 c->start = 0;
462             }
463 
464             memcpy(c->outbuf + c->stop, ANSI_PREFIX, strlen(ANSI_PREFIX));
465             c->stop += strlen(ANSI_PREFIX);
466             memcpy(c->outbuf + c->stop, server->buffer, server->buflen);
467             c->stop += server->buflen;
468 
469             return 0;
470         }
471     }
472 
473     /* We no longer have backlog, send our new data */
474 
475     /* Send ANSI prefix */
476     ret = nonblock_write(c->fd, ANSI_PREFIX, strlen(ANSI_PREFIX));
477     if(ret == -1)
478     {
479         if(errno == EAGAIN)
480             ret = 0;
481         else
482         {
483             fprintf(stderr, "[%i] failed (%s)\n", c->fd, strerror(errno));
484             return -1;
485         }
486     }
487 
488     if(ret < (ssize_t)strlen(ANSI_PREFIX))
489     {
490         if(strlen(ANSI_PREFIX) + server->buflen > OUTBUFFER)
491         {
492             /* Overflow! Empty buffer and start again */
493             memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET));
494             c->start = 0;
495             c->stop = strlen(ANSI_RESET);
496             return 0;
497         }
498 
499         memcpy(c->outbuf, ANSI_PREFIX, strlen(ANSI_PREFIX) - ret);
500         c->stop = strlen(ANSI_PREFIX) - ret;
501         memcpy(c->outbuf + c->stop, server->buffer, server->buflen);
502         c->stop += server->buflen;
503 
504         return 0;
505     }
506 
507     /* Send actual data */
508     ret = nonblock_write(c->fd, server->buffer, server->buflen);
509     if(ret == -1)
510     {
511         if(errno == EAGAIN)
512             ret = 0;
513         else
514         {
515             fprintf(stderr, "[%i] failed (%s)\n", c->fd, strerror(errno));
516             return -1;
517         }
518     }
519 
520     if(ret < (int)server->buflen)
521     {
522         if(server->buflen > OUTBUFFER)
523         {
524             /* Overflow! Empty buffer and start again */
525             memcpy(c->outbuf, ANSI_RESET, strlen(ANSI_RESET));
526             c->start = 0;
527             c->stop = strlen(ANSI_RESET);
528             return 0;
529         }
530 
531         memcpy(c->outbuf, server->buffer, server->buflen - ret);
532         c->stop = server->buflen - ret;
533 
534         return 0;
535     }
536 
537     return 0;
538 }
539 
nonblock_write(int fd,void * buf,size_t len)540 ssize_t nonblock_write(int fd, void *buf, size_t len)
541 {
542     size_t total = 0;
543     ssize_t ret;
544 
545     while(total < len)
546     {
547         do
548         {
549             ret = write(fd, buf, len);
550         }
551         while(ret < 0 && errno == EINTR);
552 
553         if(ret < 0)
554             return ret;
555         else if(ret == 0)
556             return total;
557 
558         total += len;
559         buf = (void *)((uintptr_t)buf + len);
560     }
561 
562     return total;
563 }
564 
565