"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. For more information about "cacaserver.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.99.beta19_vs_0.99.beta20.

    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