"Fossies" - the Fresh Open Source Software Archive

Member "libcaca-0.99.beta20/src/cacaserver.c" (19 Oct 2021, 16596 Bytes) of package /linux/privat/libcaca-0.99.beta20.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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