| summaryrefslogtreecommitdiff | 
diff options
| author | Nathanael Sensfelder <SpamShield0@MultiAgentSystems.org> | 2017-01-18 19:09:16 +0100 | 
|---|---|---|
| committer | Nathanael Sensfelder <SpamShield0@MultiAgentSystems.org> | 2017-01-18 19:09:16 +0100 | 
| commit | 0d49fb74eadcf933f696420cd182077927680d26 (patch) | |
| tree | 9220d260ce878f369138da12dae0300cf9ade5c9 /src/irc/network.c | |
| parent | 24afb3e60bafd98e6a83dcb41ee6a7f7d41e76bc (diff) | |
Done with 'core', starting to work on 'knowledge'.
Diffstat (limited to 'src/irc/network.c')
| -rw-r--r-- | src/irc/network.c | 568 | 
1 files changed, 568 insertions, 0 deletions
| diff --git a/src/irc/network.c b/src/irc/network.c new file mode 100644 index 0000000..edafd4f --- /dev/null +++ b/src/irc/network.c @@ -0,0 +1,568 @@ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +/* "POSIX.1  does not require the inclusion of <sys/types.h>" */ +/* - man page for setsockopt */ +/* #include <sys/types.h> */ +#include <sys/socket.h> +#include <sys/time.h> + +#include "error.h" + +#include "network.h" + +static int re_create_socket (struct ZoO_network net [const restrict static 1]) +{ +   struct timeval timeout; +   const int old_errno = errno; + +   errno = 0; +   timeout.tv_sec = ZoO_NETWORK_TIMEOUT; +   timeout.tv_usec = 0; + +   if (net->connection != -1) +   { +      close(net->connection); +   } + +   net->connection = +      socket +      ( +         net->addrinfo->ai_family, +         net->addrinfo->ai_socktype, +         net->addrinfo->ai_protocol +      ); + +   if (net->connection == -1) +   { +      ZoO_ERROR("Could not create socket: %s.", strerror(errno)); + +      goto RETURN_FAILED; +   } + +   if +   ( +      ( +         setsockopt +         ( +            net->connection, +            SOL_SOCKET, +            SO_RCVTIMEO, +            (const void *) &timeout, +            (socklen_t) sizeof(struct timeval) +         ) < 0 +      ) +      || +      ( +         setsockopt +         ( +            net->connection, +            SOL_SOCKET, +            SO_SNDTIMEO, +            (const void *) &timeout, +            (socklen_t) sizeof(struct timeval) +         ) < 0 +      ) +   ) +   { +      ZoO_ERROR("Could not set timeout on network socket: %s", strerror(errno)); + +      goto RETURN_FAILED; +   } + +   ZoO_S_DEBUG(ZoO_DEBUG_NETWORK, "(Re)connecting to network..."); + +   if +   ( +      connect +      ( +         net->connection, +         net->addrinfo->ai_addr, +         net->addrinfo->ai_addrlen +      ) != 0 +   ) +   { +      ZoO_ERROR("Could not establish connection: %s", strerror(errno)); + +      goto RETURN_FAILED; +   } + +   errno = old_errno; + +   return 0; + +RETURN_FAILED: +   errno = old_errno; + +   return -1; +} + +static int reconnect (struct ZoO_network net [const restrict static 1]) +{ +   const int old_errno = errno; + +   memset(net->in, 0, (sizeof(ZoO_char) * 513)); +   memset(net->out, 0, (sizeof(ZoO_char) * 513)); +   memset(net->buffer, 0, (sizeof(ZoO_char) * 513)); + +   net->buffer_index = 0; +   net->buffer_remaining = 0; + +   if (re_create_socket(net) < 0) +   { +      return -1; +   } + +   snprintf(net->out, 512, "USER %s 8 * :%s\r\n", net->user, net->name); + +   if (write(net->connection, net->out, strlen(net->out)) < 1) +   { +      goto RETURN_WRITE_FAILED; +   } + +   snprintf(net->out, 512, "NICK %s\r\n", net->nick); + +   if (write(net->connection, net->out, strlen(net->out)) < 1) +   { +      goto RETURN_WRITE_FAILED; +   } + +   net->buffer_remaining = 0; +   net->buffer_index = 0; + +   ZoO_S_DEBUG(ZoO_DEBUG_NETWORK, "(Re)connected."); + +   errno = old_errno; + +   return 0; + +RETURN_WRITE_FAILED: +   ZoO_ERROR +   ( +      "Unable to write to the network: %s", +      strerror(errno) +   ); + +   errno = old_errno; + +   return -1; +} + +int ZoO_network_connect +( +   struct ZoO_network net [const static 1], +   const char host [const restrict static 1], +   const char port [const restrict static 1], +   const char channel [const restrict static 1], +   const char user [const restrict static 1], +   const char name [const restrict static 1], +   const char nick [const restrict static 1] +) +{ +   int error; +   struct addrinfo hints; +   const int old_errno = errno; + +   net->connection = -1; +   net->channel = channel; +   net->user = user; +   net->name = name; +   net->nick = nick; +   net->buffer_index = 0; +   net->buffer_remaining = 0; + +   memset(&hints, 0, sizeof(struct addrinfo)); +   memset(net->in, 0, (sizeof(ZoO_char) * 513)); +   memset(net->out, 0, (sizeof(ZoO_char) * 513)); +   memset(net->buffer, 0, (sizeof(ZoO_char) * 513)); + +   hints.ai_family = AF_INET; +   hints.ai_socktype = SOCK_STREAM; + +   errno = 0; + +   error = getaddrinfo(host, port, &hints, &(net->addrinfo)); + +   if (error != 0) +   { +      if (error == EAI_SYSTEM) +      { +         ZoO_ERROR +         ( +            "Could not retrieve server information: %s.", +            strerror(errno) +         ); +      } +      else +      { +         ZoO_FATAL +         ( +            "Could not retrieve server information: %s.", +            gai_strerror(error) +         ); +      } + +      errno = old_errno; + +      return -1; +   } + +   errno = old_errno; + +   reconnect(net); + +   return 0; +} + +static void buffer_msg +( +   struct ZoO_network net [const static 1] +) +{ +   ssize_t in_count, i; + +   if (net->buffer_remaining > 0) +   { +      in_count = net->buffer_remaining; +      net->buffer_remaining = 0; + +      goto PARSE_READ; +   } + +READ_MORE: +   in_count = read(net->connection, net->buffer, 512); + +   if (in_count <= 0) +   { +      ZoO_ERROR("Could not read from network: %s", strerror(errno)); + +      while (reconnect(net) < 0) +      { +         ZoO_S_DEBUG +         ( +            ZoO_DEBUG_NETWORK, +            "Attempting new connection in 5s." +         ); +         sleep(5); +      } + +      goto READ_MORE; +   } + +PARSE_READ: +   for (i = 0; i < in_count; ++i) +   { +      net->in[net->buffer_index] = net->buffer[i]; + +      if +      ( +         (net->buffer_index > 0) +         && (net->in[net->buffer_index - 1] == '\r') +         && (net->in[net->buffer_index] == '\n') +      ) +      { +         net->buffer_remaining = (in_count - (i + 1)); +         net->in_length = (net->buffer_index - 1); +         net->buffer_index = 0; + +         if (net->buffer_remaining > 0) +         { +            memmove +            ( +               (void *) net->buffer, +               (const void *) (net->buffer + (i + 1)), +               net->buffer_remaining +            ); +         } + +         return; +      } + +      net->buffer_index += 1; + +      if (net->buffer_index > 512) +      { +         ZoO_S_WARNING("Incoming message is too long. Discarded."); + +         net->buffer_index = 0; +         net->buffer_remaining = 0; + +         break; +      } +   } + +   goto READ_MORE; +} + +void handle_ping (struct ZoO_network net [const restrict static 1]) +{ +   const int old_errno = errno; + +   #if ZoO_RANDOMLY_IGNORE_PING == 1 +   if ((rand() % 10) < 3) +   { +      ZoO_S_DEBUG(ZoO_DEBUG_NETWORK, "Ping request ignored."); + +      return; +   } + +   #endif + +   #if ZoO_DEBUG_NETWORK_PING == 1 +      net->in[net->in_length] = '\0'; + +      ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->in] %s", net->in); + +      net->in[net->in_length] = '\r'; +   #endif + +   net->in[1] = 'O'; + +   errno = 0; + +   if (write(net->connection, net->in, (net->in_length + 2)) < 1) +   { +      ZoO_ERROR("Could not reply to PING request: %s", strerror(errno)); + +      errno = old_errno; + +      while (reconnect(net) < 0) +      { +         ZoO_S_DEBUG +         ( +            ZoO_DEBUG_NETWORK, +            "Attempting new connection in 5s." +         ); +         sleep(5); +      } + +      return; +   } + +   errno = old_errno; + +#if ZoO_DEBUG_NETWORK_PING == 1 +   net->in[net->in_length] = '\0'; + +   ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->out] %s", net->in); +#endif + +} + +int ZoO_network_receive +( +   struct ZoO_network net [const restrict static 1], +   size_t msg_offset [const restrict static 1], +   size_t msg_size [const restrict static 1], +   enum ZoO_msg_type type [const restrict static 1] +) +{ +   const int old_errno = errno; +   ssize_t cmd, i; + +READ_NEW_MSG: +   buffer_msg(net); + +   net->in[net->in_length + 2] = '\0'; + +   /* XXX: doesn't that prevent net [restrict]? */ +   if (ZoO_IS_PREFIX("PING", net->in)) +   { + +      handle_ping(net); + +      goto READ_NEW_MSG; +   } + +   if (net->in_length == 0) +   { +      goto READ_NEW_MSG; +   } + +   net->in[net->in_length] = '\0'; + +   ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->in] %s", net->in); + +   if (net->in[0] == ':') +   { +      cmd = 0; + +      for (i = 1; i < 512; i++) +      { +         if (net->in[i] == ' ') +         { +            cmd = (i + 1); + +            break; +         } +      } + +      if (ZoO_IS_PREFIX("001", (net->in + cmd))) +      { +         snprintf +         ( +            net->out, +            512, +            "JOIN :%s\r\n", +            net->channel +         ); + +         errno = 0; + +         if (write(net->connection, net->out, strlen(net->out)) < 1) +         { +            ZoO_ERROR +            ( +               "Could not send JOIN request: %s", +               strerror(errno) +            ); + +            errno = old_errno; + +            if (reconnect(net) < 0) +            { +               return -1; +            } +         } + +         errno = old_errno; + +         ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->out] %s", net->out); + +         goto READ_NEW_MSG; +      } + +      if (ZoO_IS_PREFIX("JOIN", (net->in + cmd))) +      { +         for (i = 1; (i < 512) && (net->in[i] != '!'); ++i) +         { +         } + +         if ((i == 512) || (i == 1)) +         { +            ZoO_ERROR("Could not find JOIN username: %s", net->in); + +            goto READ_NEW_MSG; +         } + +         *msg_offset = 1; +         *msg_size = (i - 1); +         net->in[i] = '\0'; + +         *type = ZoO_JOIN; + +         return 0; +      } + +      if (ZoO_IS_PREFIX("PRIVMSG", (net->in + cmd))) +      { + +         for (; i < 512; i++) +         { +            if (net->in[i] == ':') +            { +               cmd = (i + 1); + +               break; +            } +         } + +         *msg_offset = cmd; +         *msg_size = (net->in_length - cmd); + +         /*net->in[*msg_size - 1] = '\0'; */ + +         *type = ZoO_PRIVMSG; + +         return 0; +      } +   } + +   if (ZoO_IS_PREFIX("ERROR", (net->in + cmd))) +   { +      while (reconnect(net) < 0) +      { +         ZoO_S_DEBUG +         ( +            ZoO_DEBUG_NETWORK, +            "Attempting new connection in 5s." +         ); +         sleep(5); +      } +   } + +   goto READ_NEW_MSG; +} + +int ZoO_network_send (struct ZoO_network net [const restrict static 1]) +{ +   int const old_errno = errno; + +   if (ZoO_IS_PREFIX("\001action", net->out)) +   { + +      net->out[1] = 'A'; +      net->out[2] = 'C'; +      net->out[3] = 'T'; +      net->out[4] = 'I'; +      net->out[5] = 'O'; +      net->out[6] = 'N'; + +      snprintf +      ( +         net->in, +         512, +         "PRIVMSG %s :%s\001\r\n", +         net->channel, +         net->out +      ); +   } +   else +   { +      snprintf +      ( +         net->in, +         512, +         "PRIVMSG %s :%s\r\n", +         net->channel, +         net->out +      ); +   } + +   errno = 0; + +   if (write(net->connection, net->in, strlen(net->in)) < 1) +   { +      ZoO_ERROR +      ( +         "Could not send PRIVMSG: %s.", +         strerror(errno) +      ); + +      errno = old_errno; + +      if (reconnect(net) < 0) +      { +         return -2; +      } +      else +      { +         return -1; +      } +   } + +   errno = old_errno; + +   ZoO_DEBUG(ZoO_DEBUG_NETWORK, "[NET->out] %s", net->in); + +   return 0; +} + +void ZoO_network_disconnect (struct ZoO_network net [const restrict static 1]) +{ +   freeaddrinfo(net->addrinfo); +   close(net->connection); +} + | 


