Ejemplo n.º 1
0
/** Send resource usage statistics to a client.
 * @param cptr Client to send data to.
 * @param sd StatDesc that generated the stats request (ignored).
 * @param param Extra parameter from user (ignored).
 */
void send_usage(struct Client *cptr, const struct StatDesc *sd,
                char *param)
{
  os_get_rusage(cptr, CurrentTime - cli_since(&me), debug_enumerator);

  send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":DBUF alloc %u used %u",
	     DBufAllocCount, DBufUsedCount);
}
Ejemplo n.º 2
0
static void
stats_uptime(struct Client* to, struct StatDesc* sd, int stat, char* param)
{
  time_t nowr;

  nowr = CurrentTime - cli_since(&me);
  send_reply(to, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
	     (nowr / 60) % 60, nowr % 60);
  send_reply(to, RPL_STATSCONN, max_connection_count, max_client_count);
}
Ejemplo n.º 3
0
/** Check whether a new connection from a local client should be allowed.
 * A connection is rejected if someone from the "same" address (see
 * ip_registry_find()) connects IPCHECK_CLONE_LIMIT times, each time
 * separated by no more than IPCHECK_CLONE_PERIOD seconds.
 * @param[in] addr Address of client.
 * @param[out] next_target_out Receives time to grant another free target.
 * @return Non-zero if the connection is permitted, zero if denied.
 */
int ip_registry_check_local(const struct irc_in_addr *addr, time_t* next_target_out)
{
  struct IPRegistryEntry* entry = ip_registry_find(addr);
  unsigned int free_targets = STARTTARGETS;

  if (0 == entry) {
    entry       = ip_registry_new_entry();
    ip_registry_canonicalize(&entry->addr, addr);
    ip_registry_add(entry);
    Debug((DEBUG_DNS, "IPcheck added new registry for local connection from %s.", ircd_ntoa(&entry->addr)));
    return 1;
  }
  /* Note that this also counts server connects.
   * It is hard and not interesting, to change that.
   * Refuse connection if it would overflow the counter.
   */
  if (0 == ++entry->connected)
  {
    entry->connected--;
    Debug((DEBUG_DNS, "IPcheck refusing local connection from %s: counter overflow.", ircd_ntoa(&entry->addr)));
    return 0;
  }

  if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_PERIOD)
    entry->attempts = 0;

  free_targets = ip_registry_update_free_targets(entry);
  entry->last_connect = NOW;

  if (0 == ++entry->attempts)   /* Check for overflow */
    --entry->attempts;

  if (entry->attempts < IPCHECK_CLONE_LIMIT) {
    if (next_target_out)
      *next_target_out = CurrentTime - (TARGET_DELAY * free_targets - 1);
  }
  else if ((CurrentTime - cli_since(&me)) > IPCHECK_CLONE_DELAY) {
    /*
     * Don't refuse connection when we just rebooted the server
     */
#ifndef NOTHROTTLE
    assert(entry->connected > 0);
    --entry->connected;
    Debug((DEBUG_DNS, "IPcheck refusing local connection from %s: too fast.", ircd_ntoa(&entry->addr)));
    return 0;
#endif
  }
  Debug((DEBUG_DNS, "IPcheck accepting local connection from %s.", ircd_ntoa(&entry->addr)));
  return 1;
}
Ejemplo n.º 4
0
/*
 * release_auth_client - release auth client from auth system
 * this adds the client into the local client lists so it can be read by
 * the main io processing loop
 */
static void release_auth_client(struct Client* client)
{
  assert(0 != client);
  cli_auth(client) = 0;
  cli_lasttime(client) = cli_since(client) = CurrentTime;
  if (cli_fd(client) > HighestFd)
    HighestFd = cli_fd(client);
  LocalClientArray[cli_fd(client)] = client;

  add_client_to_list(client);
  socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
  Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
         cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
}
Ejemplo n.º 5
0
/*
 * Create a new struct Client structure and set it to initial state.
 *
 *   from == NULL,   create local client (a client connected to a socket).
 *
 *   from != NULL,   create remote client (behind a socket associated with
 *                   the client defined by 'from').
 *                   ('from' is a local client!!).
 */
struct Client* make_client(struct Client *from, int status)
{
  struct Client* cptr = 0;
  struct Connection* con = 0;

  assert(!from || cli_verify(from));

  cptr = alloc_client();

  assert(0 != cptr);
  assert(!cli_magic(cptr));
  assert(0 == from || 0 != cli_connect(from));

  if (!from) { /* local client, allocate a struct Connection */
    con = alloc_connection();

    assert(0 != con);
    assert(!con_magic(con));

    con_magic(con) = CONNECTION_MAGIC;
    con_fd(con) = -1; /* initialize struct Connection */
    con_freeflag(con) = 0;
    con_nextnick(con) = CurrentTime - NICK_DELAY;
    con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1));
    con_handler(con) = UNREGISTERED_HANDLER;
    con_client(con) = cptr;

    cli_local(cptr) = 1; /* Set certain fields of the struct Client */
    cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime;
    cli_lastnick(cptr) = TStime();
  } else
    con = cli_connect(from); /* use 'from's connection */

  assert(0 != con);
  assert(con_verify(con));

  cli_magic(cptr) = CLIENT_MAGIC;
  cli_connect(cptr) = con; /* set the connection and other fields */
  cli_status(cptr) = status;
  cli_hnext(cptr) = cptr;
  strcpy(cli_username(cptr), "unknown");

  return cptr;
}
Ejemplo n.º 6
0
/** Parse a line of data from a user.
 * NOTE: parse_*() should not be called recursively by any other
 * functions!
 * @param[in] cptr Client that sent the data.
 * @param[in] buffer Start of input line.
 * @param[in] bufend End of input line.
 * @return 0 on success, -1 on parse error, or CPTR_KILLED if message
 * handler returns it.
 */
int
parse_client(struct Client *cptr, char *buffer, char *bufend)
{
  struct Client*  from = cptr;
  char*           ch;
  char*           s;
  int             i;
  int             paramcount;
  int             isshun = 0;
  int             lagmin = -1;
  int             lagfactor = -1;
  struct Message* mptr;
  MessageHandler  handler = 0;

  Debug((DEBUG_DEBUG, "Client Parsing: %s", buffer));

  if (IsDead(cptr))
    return 0;

  para[0] = cli_name(from);
  for (ch = buffer; *ch == ' '; ch++);  /* Eat leading spaces */
  if (*ch == ':')               /* Is any client doing this ? */
  {
    for (++ch; *ch && *ch != ' '; ++ch)
      ; /* Ignore sender prefix from client */
    while (*ch == ' ')
      ch++;                     /* Advance to command */
  }
  if (*ch == '\0')
  {
    ServerStats->is_empt++;
    Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
        cli_name(cptr), cli_name(from)));
    return (-1);
  }

  if ((s = strchr(ch, ' ')))
    *s++ = '\0';

  expire_shuns();
  if (IsRegistered(cptr)) {
    if (cli_user(cptr)->username && cli_user(cptr)->host) {
      if (shun_lookup(cptr, 0))
        isshun = 1;
    }
  }

  if ((mptr = msg_tree_parse(ch, &msg_tree)) == NULL)
  {
    /*
     * Note: Give error message *only* to recognized
     * persons. It's a nightmare situation to have
     * two programs sending "Unknown command"'s or
     * equivalent to each other at full blast....
     * If it has got to person state, it at least
     * seems to be well behaving. Perhaps this message
     * should never be generated, though...  --msa
     * Hm, when is the buffer empty -- if a command
     * code has been found ?? -Armin
     */
    if (buffer[0] != '\0' && !isshun)
    {
      if (IsUser(from))
	send_reply(from, ERR_UNKNOWNCOMMAND, ch);
      Debug((DEBUG_ERROR, "Unknown (%s) from %s",
            ch, get_client_name(cptr, HIDE_IP)));
    }
    ServerStats->is_unco++;
    return (-1);
  }

  if (isshun && !(mptr->flags & MFLG_NOSHUN))
    return 0;

  paramcount = mptr->parameters;
  i = bufend - ((s) ? s : ch);
  mptr->bytes += i;
  lagmin = get_lag_min(cptr);
  lagfactor = get_lag_factor(cptr);
  if (lagmin < 0)
    lagmin = 2;
  if (lagfactor < 0)
    lagfactor = 120;
  if (((mptr->flags & MFLG_SLOW) || !IsAnOper(cptr)) && lagfactor > 0)
    cli_since(cptr) += (lagmin + i / lagfactor);
  /*
   * Allow only 1 msg per 2 seconds
   * (on average) to prevent dumping.
   * to keep the response rate up,
   * bursts of up to 5 msgs are allowed
   * -SRB
   */

  /*
   * Must the following loop really be so devious? On
   * surface it splits the message to parameters from
   * blank spaces. But, if paramcount has been reached,
   * the rest of the message goes into this last parameter
   * (about same effect as ":" has...) --msa
   */

  /* Note initially true: s==NULL || *(s-1) == '\0' !! */

  if (mptr->flags & MFLG_EXTRA) {
    /* This is a horrid kludge to avoid changing the command handler
     * argument list. */
    para[1] = (char*)mptr->extra;
    i = 1;
  } else {
    i = 0;
  }
  if (s)
  {
    if (paramcount > MAXPARA)
      paramcount = MAXPARA;
    for (;;)
    {
      /*
       * Never "FRANCE " again!! ;-) Clean
       * out *all* blanks.. --msa
       */
      while (*s == ' ')
        *s++ = '\0';

      if (*s == '\0')
        break;
      if (*s == ':')
      {
        /*
         * The rest is single parameter--can
         * include blanks also.
         */
        para[++i] = s + 1;
        break;
      }
      para[++i] = s;
      if (i >= paramcount)
        break;
      for (; *s != ' ' && *s; s++);
    }
  }
  para[++i] = NULL;
  ++mptr->count;

  handler = mptr->handlers[cli_handler(cptr)];
  assert(0 != handler);

  if (!feature_bool(FEAT_IDLE_FROM_MSG) && IsUser(cptr) &&
      handler != m_ping && handler != m_ignore)
    cli_user(from)->last = CurrentTime;

  return (*handler) (cptr, from, i, para);
}
Ejemplo n.º 7
0
/** Run the daemon.
 * @param[in] argc Number of arguments in \a argv.
 * @param[in] argv Arguments to program execution.
 */
int main(int argc, char **argv) {
  CurrentTime = time(NULL);

  thisServer.argc = argc;
  thisServer.argv = argv;
  thisServer.uid  = getuid();
  thisServer.euid = geteuid();

#ifdef MDEBUG
  mem_dbg_initialise();
#endif

#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
  set_core_limit();
#endif

  umask(077);                   /* better safe than sorry --SRB */
  memset(&me, 0, sizeof(me));
  memset(&me_con, 0, sizeof(me_con));
  cli_connect(&me) = &me_con;
  cli_fd(&me) = -1;

  parse_command_line(argc, argv);

  if (chdir(dpath)) {
    fprintf(stderr, "Fail: Cannot chdir(%s): %s, check DPATH\n", dpath, strerror(errno));
    return 2;
  }

  if (!set_userid_if_needed())
    return 3;

  /* Check paths for accessibility */
  if (!check_file_access(SPATH, 'S', X_OK) ||
      !check_file_access(configfile, 'C', R_OK))
    return 4;

  if (!init_connection_limits())
    return 9;

  close_connections(!(thisServer.bootopt & (BOOT_DEBUG | BOOT_TTY | BOOT_CHKCONF)));

  /* daemon_init() must be before event_init() because kqueue() FDs
   * are, perversely, not inherited across fork().
   */
  daemon_init(thisServer.bootopt & BOOT_TTY);

#ifdef DEBUGMODE
  /* Must reserve fd 2... */
  if (debuglevel >= 0 && !(thisServer.bootopt & BOOT_TTY)) {
    int fd;
    if ((fd = open("/dev/null", O_WRONLY)) < 0) {
      fprintf(stderr, "Unable to open /dev/null (to reserve fd 2): %s\n",
	      strerror(errno));
      return 8;
    }
    if (fd != 2 && dup2(fd, 2) < 0) {
      fprintf(stderr, "Unable to reserve fd 2; dup2 said: %s\n",
	      strerror(errno));
      return 8;
    }
  }
#endif

  event_init(MAXCONNECTIONS);

  setup_signals();
  feature_init(); /* initialize features... */
  log_init(*argv);
  set_nomem_handler(outofmemory);

  initload();
  init_list();
  init_hash();
  init_class();
  initwhowas();
  initmsgtree();
  initstats();

  /* we need this for now, when we're modular this 
     should be removed -- hikari */
  ircd_crypt_init();

  motd_init();

  if (!init_conf()) {
    log_write(LS_SYSTEM, L_CRIT, 0, "Failed to read configuration file %s",
	      configfile);
    return 7;
  }

  if (thisServer.bootopt & BOOT_CHKCONF) {
    if (dbg_client)
      conf_debug_iline(dbg_client);
    fprintf(stderr, "Configuration file %s checked okay.\n", configfile);
    return 0;
  }

  debug_init(thisServer.bootopt & BOOT_TTY);
  if (check_pid()) {
    Debug((DEBUG_FATAL, "Failed to acquire PID file lock after fork"));
    exit(2);
  }

  init_server_identity();

  uping_init();

  stats_init();

  IPcheck_init();
  timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1);
  timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1);
  timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60);
  timer_add(timer_init(&mute_timer), check_expired_mutes, 0, TT_PERIODIC, 30);

  CurrentTime = time(NULL);

  SetMe(&me);
  cli_magic(&me) = CLIENT_MAGIC;
  cli_from(&me) = &me;
  make_server(&me);

  cli_serv(&me)->timestamp = TStime();  /* Abuse own link timestamp as start TS */
  cli_serv(&me)->prot      = atoi(MAJOR_PROTOCOL);
  cli_serv(&me)->up        = &me;
  cli_serv(&me)->down      = NULL;
  cli_handler(&me)         = SERVER_HANDLER;

  SetYXXCapacity(&me, MAXCLIENTS);

  cli_lasttime(&me) = cli_since(&me) = cli_firsttime(&me) = CurrentTime;

  hAddClient(&me);

  write_pidfile();
  init_counters();

  Debug((DEBUG_NOTICE, "Server ready..."));
  log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready");

  event_loop();

  return 0;
}
Ejemplo n.º 8
0
/** Read a 'packet' of data from a connection and process it.  Read in
 * 8k chunks to give a better performance rating (for server
 * connections).  Do some tricky stuff for client connections to make
 * sure they don't do any flooding >:-) -avalon
 * @param cptr Client from which to read data.
 * @param socket_ready If non-zero, more data can be read from the client's socket.
 * @return Positive number on success, zero on connection-fatal failure, negative
 *   if user is killed.
 */
static int read_packet(struct Client *cptr, int socket_ready)
{
  unsigned int dolen = 0;
  unsigned int length = 0;

  if (socket_ready &&
      !(IsUser(cptr) &&
	DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD))) {
#if defined(USE_SSL)
    switch (client_recv(cptr, readbuf, sizeof(readbuf), &length)) {
#else
    switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) {
#endif
    case IO_SUCCESS:
      if (length)
      {
        cli_lasttime(cptr) = CurrentTime;
        ClearPingSent(cptr);
        ClrFlag(cptr, FLAG_NONL);
        if (cli_lasttime(cptr) > cli_since(cptr))
          cli_since(cptr) = cli_lasttime(cptr);
      }
      break;
    case IO_BLOCKED:
      break;
    case IO_FAILURE:
      cli_error(cptr) = errno;
      /* SetFlag(cptr, FLAG_DEADSOCKET); */
      return 0;
    }
  }

  /*
   * For server connections, we process as many as we can without
   * worrying about the time of day or anything :)
   */
  if (length > 0 && IsServer(cptr))
    return server_dopacket(cptr, readbuf, length);
  else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr)))
    return connect_dopacket(cptr, readbuf, length);
  else
  {
    /*
     * Before we even think of parsing what we just read, stick
     * it on the end of the receive queue and do it when its
     * turn comes around.
     */
    if (length > 0 && dbuf_put(cptr, &(cli_recvQ(cptr)), readbuf, length) == 0)
      return exit_client(cptr, cptr, &me, "dbuf_put fail");

    if ((DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD))
         && !IsChannelService(cptr))
      return exit_client(cptr, cptr, &me, "Excess Flood");

    while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) &&
           (IsTrusted(cptr) || IsChannelService(cptr) || cli_since(cptr) - CurrentTime < 10))
    {
      dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE);
      /*
       * Devious looking...whats it do ? well..if a client
       * sends a *long* message without any CR or LF, then
       * dbuf_getmsg fails and we pull it out using this
       * loop which just gets the next 512 bytes and then
       * deletes the rest of the buffer contents.
       * -avalon
       */
      if (dolen == 0)
      {
        if (DBufLength(&(cli_recvQ(cptr))) < 510)
          SetFlag(cptr, FLAG_NONL);
        else
        {
          /* More than 512 bytes in the line - drop the input and yell
           * at the client.
           */
          DBufClear(&(cli_recvQ(cptr)));
          send_reply(cptr, ERR_INPUTTOOLONG);
        }
      }
      else if (client_dopacket(cptr, dolen) == CPTR_KILLED)
        return CPTR_KILLED;
      /*
       * If it has become registered as a Server
       * then skip the per-message parsing below.
       */
      if (IsHandshake(cptr) || IsServer(cptr))
      {
        while (-1)
        {
          dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf));
          if (dolen <= 0)
            return 1;
          else if (dolen == 0)
          {
            if (DBufLength(&(cli_recvQ(cptr))) < 510)
              SetFlag(cptr, FLAG_NONL);
            else {
              DBufClear(&(cli_recvQ(cptr)));
              /* send_reply(cptr, ERR_INPUTTOOLONG); */
            }
          }
          else if ((IsServer(cptr) &&
                    server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) ||
                   (!IsServer(cptr) &&
                    connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED))
            return CPTR_KILLED;
        }
      }
    }

    /* If there's still data to process, wait 2 seconds first */
    if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) &&
	!t_onqueue(&(cli_proc(cptr))))
    {
      Debug((DEBUG_LIST, "Adding client process timer for %C", cptr));
      cli_freeflag(cptr) |= FREEFLAG_TIMER;
      timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr),
		TT_RELATIVE, 2);
    }
  }
  return 1;
}

/** Start a connection to another server.
 * @param aconf Connect block data for target server.
 * @param by Client who requested the connection (if any).
 * @return Non-zero on success; zero on failure.
 */
int connect_server(struct ConfItem* aconf, struct Client* by)
{
  struct Client*   cptr = 0;
  assert(0 != aconf);

  if (aconf->dns_pending) {
    sendto_opmask(0, SNO_OLDSNO, "Server %s connect DNS pending",
                  aconf->name);
    return 0;
  }
  Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name,
         ircd_ntoa(&aconf->address.addr)));

  if ((cptr = FindClient(aconf->name))) {
    if (IsServer(cptr) || IsMe(cptr)) {
      sendto_opmask(0, SNO_OLDSNO, "Server %s already present from %s",
                    aconf->name, cli_name(cli_from(cptr)));
      if (by && IsUser(by) && !MyUser(by)) {
        sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present "
                      "from %s", by, aconf->name, cli_name(cli_from(cptr)));
      }
      return 0;
    }
    else if (IsHandshake(cptr) || IsConnecting(cptr)) {
      if (by && IsUser(by)) {
        sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in "
                      "progress", by, cli_name(cptr));
      }
      return 0;
    }
  }
  /*
   * If we don't know the IP# for this host and it is a hostname and
   * not a ip# string, then try and find the appropriate host record.
   */
  if (!irc_in_addr_valid(&aconf->address.addr)
      && !ircd_aton(&aconf->address.addr, aconf->host)) {
    char buf[HOSTLEN + 1];

    host_from_uh(buf, aconf->host, HOSTLEN);
    gethost_byname(buf, connect_dns_callback, aconf);
    aconf->dns_pending = 1;
    return 0;
  }
  cptr = make_client(NULL, STAT_UNKNOWN_SERVER);

  /*
   * Copy these in so we have something for error detection.
   */
  ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN);
  ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN);

  /*
   * Attach config entries to client here rather than in
   * completed_connection. This to avoid null pointer references
   */
  attach_confs_byhost(cptr, aconf->host, CONF_SERVER);

  if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) {
    sendto_opmask(0, SNO_OLDSNO, "Host %s is not enabled for "
                  "connecting: no Connect block", aconf->name);
    if (by && IsUser(by) && !MyUser(by)) {
      sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no "
                    "Connect block", by, aconf->name);
    }
    det_confs_butmask(cptr, 0);
    free_client(cptr);
    return 0;
  }
  /*
   * attempt to connect to the server in the conf line
   */
  if (!connect_inet(aconf, cptr)) {
    if (by && IsUser(by) && !MyUser(by)) {
      sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by,
                    cli_name(cptr));
    }
    det_confs_butmask(cptr, 0);
    free_client(cptr);
    return 0;
  }
  /*
   * NOTE: if we're here we have a valid C:Line and the client should
   * have started the connection and stored the remote address/port and
   * ip address name in itself
   *
   * The socket has been connected or connect is in progress.
   */
  make_server(cptr);
  if (by && IsUser(by)) {
    ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s",
		  NumNick(by));
    assert(0 == cli_serv(cptr)->user);
    cli_serv(cptr)->user = cli_user(by);
    cli_user(by)->refcnt++;
  }
  else {
    *(cli_serv(cptr))->by = '\0';
    /* strcpy(cptr->serv->by, "Auto"); */
  }
  cli_serv(cptr)->up = &me;
  SetConnecting(cptr);

  if (cli_fd(cptr) > HighestFd)
    HighestFd = cli_fd(cptr);

  LocalClientArray[cli_fd(cptr)] = cptr;

  Count_newunknown(UserStats);
  /* Actually we lie, the connect hasn't succeeded yet, but we have a valid
   * cptr, so we register it now.
   * Maybe these two calls should be merged.
   */
  add_client_to_list(cptr);
  hAddClient(cptr);
/*    nextping = CurrentTime; */

  return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ?
    completed_connection(cptr) : 1;
}

/** Find the real hostname for the host running the server (or one which
 * matches the server's name) and its primary IP#.  Hostname is stored
 * in the client structure passed as a pointer.
 */
void init_server_identity(void)
{
  const struct LocalConf* conf = conf_get_local();
  assert(0 != conf);

  ircd_strncpy(cli_name(&me), conf->name, HOSTLEN);
  SetYXXServerName(&me, conf->numeric);
}

/** Process events on a client socket.
 * @param ev Socket event structure that has a struct Connection as
 *   its associated data.
 */
static void client_sock_callback(struct Event* ev)
{
  struct Client* cptr;
  struct Connection* con;
  char *fmt = "%s";
  char *fallback = 0;

  assert(0 != ev_socket(ev));
  assert(0 != s_data(ev_socket(ev)));

  con = (struct Connection*) s_data(ev_socket(ev));

  assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);

  cptr = con_client(con);

  assert(0 == cptr || con == cli_connect(cptr));

  switch (ev_type(ev)) {
  case ET_DESTROY:
    con_freeflag(con) &= ~FREEFLAG_SOCKET;

    if (!con_freeflag(con) && !cptr)
      free_connection(con);
#if defined(USE_SSL)
    ssl_free(ev_socket(ev));
#endif
    break;

  case ET_CONNECT: /* socket connection completed */
    if (!completed_connection(cptr) || IsDead(cptr))
      fallback = cli_info(cptr);
    break;

  case ET_ERROR: /* an error occurred */
    fallback = cli_info(cptr);
    cli_error(cptr) = ev_data(ev);
    /* If the OS told us we have a bad file descriptor, we should
     * record that for future reference.
     */
    if (cli_error(cptr) == EBADF)
      cli_fd(cptr) = -1;
    if (s_state(&(con_socket(con))) == SS_CONNECTING) {
      completed_connection(cptr);
      /* for some reason, the os_get_sockerr() in completed_connection()
       * can return 0 even when ev_data(ev) indicates a real error, so
       * re-assign the client error here.
       */
      cli_error(cptr) = ev_data(ev);
      break;
    }
    /*FALLTHROUGH*/
  case ET_EOF: /* end of file on socket */
    Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d", cli_fd(cptr),
	   cli_error(cptr)));
    SetFlag(cptr, FLAG_DEADSOCKET);
    if ((IsServer(cptr) || IsHandshake(cptr)) && cli_error(cptr) == 0) {
      exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)",
		      cli_name(cptr), cli_serv(cptr)->last_error_msg);
      return;
    } else {
      fmt = "Read error: %s";
      fallback = "EOF from client";
    }
    break;

  case ET_WRITE: /* socket is writable */
    ClrFlag(cptr, FLAG_BLOCKED);
    if (cli_listing(cptr) && MsgQLength(&(cli_sendQ(cptr))) < 2048)
      list_next_channels(cptr);
    Debug((DEBUG_SEND, "Sending queued data to %C", cptr));
    send_queued(cptr);
    break;

  case ET_READ: /* socket is readable */
    if (!IsDead(cptr)) {
      Debug((DEBUG_DEBUG, "Reading data from %C", cptr));
      if (read_packet(cptr, 1) == 0) /* error while reading packet */
	fallback = "EOF from client";
    }
    break;

  default:
    assert(0 && "Unrecognized socket event in client_sock_callback()");
    break;
  }

  assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr));

  if (fallback) {
    const char* msg = (cli_error(cptr)) ? strerror(cli_error(cptr)) : fallback;
    if (!msg)
      msg = "Unknown error";
    exit_client_msg(cptr, cptr, &me, fmt, msg);
  }
}

/** Process a timer on client socket.
 * @param ev Timer event that has a struct Connection as its
 * associated data.
 */
static void client_timer_callback(struct Event* ev)
{
  struct Client* cptr;
  struct Connection* con;

  assert(0 != ev_timer(ev));
  assert(0 != t_data(ev_timer(ev)));
  assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev));

  con = (struct Connection*) t_data(ev_timer(ev));

  assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);

  cptr = con_client(con);

  assert(0 == cptr || con == cli_connect(cptr));

  if (ev_type(ev)== ET_DESTROY) {
    con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */

    if (!con_freeflag(con) && !cptr)
      free_connection(con); /* client is being destroyed */
  } else {
    Debug((DEBUG_LIST, "Client process timer for %C expired; processing",
	   cptr));
    read_packet(cptr, 0); /* read_packet will re-add timer if needed */
  }

  assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr));
}
Ejemplo n.º 9
0
/** Attempt to send a sequence of bytes to the connection.
 * As a side effect, updates \a cptr's FLAG_BLOCKED setting
 * and sendB/sendK fields.
 * @param cptr Client that should receive data.
 * @param buf Message buffer to send to client.
 * @return Negative on connection-fatal error; otherwise
 *  number of bytes sent.
 */
unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf)
{
  unsigned int bytes_written = 0;
  unsigned int bytes_count = 0;
  assert(0 != cptr);

#if defined(USE_SSL)
  switch (client_sendv(cptr, buf, &bytes_count, &bytes_written)) {
#else
  switch (os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written)) {
#endif
  case IO_SUCCESS:
    ClrFlag(cptr, FLAG_BLOCKED);

    cli_sendB(cptr) += bytes_written;
    cli_sendB(&me)  += bytes_written;
    /* A partial write implies that future writes will block. */
    if (bytes_written < bytes_count)
      SetFlag(cptr, FLAG_BLOCKED);
    break;
  case IO_BLOCKED:
    SetFlag(cptr, FLAG_BLOCKED);
    break;
  case IO_FAILURE:
    cli_error(cptr) = errno;
    SetFlag(cptr, FLAG_DEADSOCKET);
    break;
  }
  return bytes_written;
}

/** Complete non-blocking connect()-sequence. Check access and
 * terminate connection, if trouble detected.
 * @param cptr Client to which we have connected, with all ConfItem structs attached.
 * @return Zero on failure (caller should exit_client()), non-zero on success.
 */
static int completed_connection(struct Client* cptr)
{
  struct ConfItem *aconf;
  time_t newts;
  struct Client *acptr;
  int i;
#if defined(USE_SSL)
  char *sslfp;
  int r;
#endif

  assert(0 != cptr);

  /*
   * get the socket status from the fd first to check if
   * connection actually succeeded
   */
  if ((cli_error(cptr) = os_get_sockerr(cli_fd(cptr)))) {
    const char* msg = strerror(cli_error(cptr));
    if (!msg)
      msg = "Unknown error";
    sendto_opmask(0, SNO_OLDSNO, "Connection failed to %s: %s",
                  cli_name(cptr), msg);
    return 0;
  }
  if (!(aconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER))) {
    sendto_opmask(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr));
    return 0;
  }

#if defined(USE_SSL)
  if (aconf->flags & CONF_SSL) {
    r = ssl_connect(&(cli_socket(cptr)));
    if (r == -1) {
      sendto_opmask(0, SNO_OLDSNO, "Connection failed to %s: SSL error",
                    cli_name(cptr));
      return 0;
    } else if (r == 0)
      return 1;
    sslfp = ssl_get_fingerprint(cli_socket(cptr).s_ssl);
    if (sslfp)
      ircd_strncpy(cli_sslclifp(cptr), sslfp, BUFSIZE+1);
    SetSSL(cptr);
  }
#endif

  if (s_state(&(cli_socket(cptr))) == SS_CONNECTING)
    socket_state(&(cli_socket(cptr)), SS_CONNECTED);

  if (!EmptyString(aconf->passwd))
    sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd);

  /*
   * Create a unique timestamp
   */
  newts = TStime();
  for (i = HighestFd; i > -1; --i) {
    if ((acptr = LocalClientArray[i]) &&
        (IsServer(acptr) || IsHandshake(acptr))) {
      if (cli_serv(acptr)->timestamp >= newts)
        newts = cli_serv(acptr)->timestamp + 1;
    }
  }
  assert(0 != cli_serv(cptr));

  cli_serv(cptr)->timestamp = newts;
  SetHandshake(cptr);
  /*
   * Make us timeout after twice the timeout for DNS look ups
   */
  cli_lasttime(cptr) = CurrentTime;
  ClearPingSent(cptr);

/* TODO: NEGOCIACION
  envia_config_req(cptr);
*/

  sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s",
                cli_name(&me), cli_serv(&me)->timestamp, newts,
		MAJOR_PROTOCOL, NumServCap(&me),
		feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me));

#if defined(DDB)
  ddb_burst(cptr);
#endif

  return (IsDead(cptr)) ? 0 : 1;
}

/** Close the physical connection.  Side effects: MyConnect(cptr)
 * becomes false and cptr->from becomes NULL.
 * @param cptr Client to disconnect.
 */
void close_connection(struct Client *cptr)
{
  struct ConfItem* aconf;

  if (IsServer(cptr)) {
    ServerStats->is_sv++;
    ServerStats->is_sbs += cli_sendB(cptr);
    ServerStats->is_sbr += cli_receiveB(cptr);
    ServerStats->is_sti += CurrentTime - cli_firsttime(cptr);
    /*
     * If the connection has been up for a long amount of time, schedule
     * a 'quick' reconnect, else reset the next-connect cycle.
     */
    if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) {
      /*
       * Reschedule a faster reconnect, if this was a automatically
       * connected configuration entry. (Note that if we have had
       * a rehash in between, the status has been changed to
       * CONF_ILLEGAL). But only do this if it was a "good" link.
       */
      aconf->hold = CurrentTime;
      aconf->hold += ((aconf->hold - cli_since(cptr) >
		       feature_int(FEAT_HANGONGOODLINK)) ?
		      feature_int(FEAT_HANGONRETRYDELAY) : ConfConFreq(aconf));
/*        if (nextconnect > aconf->hold) */
/*          nextconnect = aconf->hold; */
    }
  }
  else if (IsUser(cptr)) {
    ServerStats->is_cl++;
    ServerStats->is_cbs += cli_sendB(cptr);
    ServerStats->is_cbr += cli_receiveB(cptr);
    ServerStats->is_cti += CurrentTime - cli_firsttime(cptr);
  }
  else
    ServerStats->is_ni++;

#if defined(USE_ZLIB)
  /*
   * Siempre es una conexion nuestra
   */
  if (cli_connect(cptr)->zlib_negociation & ZLIB_IN) {
    inflateEnd(cli_connect(cptr)->comp_in);
    MyFree(cli_connect(cptr)->comp_in);
  }
  if (cli_connect(cptr)->zlib_negociation & ZLIB_OUT) {
    deflateEnd(cli_connect(cptr)->comp_out);
    MyFree(cli_connect(cptr)->comp_out);
  }
#endif

  if (-1 < cli_fd(cptr)) {
    flush_connections(cptr);
    LocalClientArray[cli_fd(cptr)] = 0;
    close(cli_fd(cptr));
    socket_del(&(cli_socket(cptr))); /* queue a socket delete */
    cli_fd(cptr) = -1;
    cli_freeflag(cptr) &= ~FREEFLAG_SOCKET;
  }
  SetFlag(cptr, FLAG_DEADSOCKET);

  MsgQClear(&(cli_sendQ(cptr)));
  client_drop_sendq(cli_connect(cptr));
  DBufClear(&(cli_recvQ(cptr)));
  memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr)));
  set_snomask(cptr, 0, SNO_SET);

  det_confs_butmask(cptr, 0);

  if (cli_listener(cptr)) {
    release_listener(cli_listener(cptr));
    cli_listener(cptr) = 0;
  }

  for ( ; HighestFd > 0; --HighestFd) {
    if (LocalClientArray[HighestFd])
      break;
  }
}

/** Close all unregistered connections.
 * @param source Oper who requested the close.
 * @return Number of closed connections.
 */
int net_close_unregistered_connections(struct Client* source)
{
  int            i;
  struct Client* cptr;
  int            count = 0;
  assert(0 != source);

  for (i = HighestFd; i > 0; --i) {
    if ((cptr = LocalClientArray[i]) && !IsRegistered(cptr)) {
      send_reply(source, RPL_CLOSING, get_client_name(source, HIDE_IP));
      exit_client(source, cptr, &me, "Oper Closing");
      ++count;
    }
  }
  return count;
}
Ejemplo n.º 10
0
/** Read a 'packet' of data from a connection and process it.  Read in
 * 8k chunks to give a better performance rating (for server
 * connections).  Do some tricky stuff for client connections to make
 * sure they don't do any flooding >:-) -avalon
 * @param cptr Client from which to read data.
 * @param socket_ready If non-zero, more data can be read from the client's socket.
 * @return Positive number on success, zero on connection-fatal failure, negative
 *   if user is killed.
 */
static int read_packet(struct Client *cptr, int socket_ready)
{
  unsigned int dolen = 0;
  unsigned int length = 0;

  if (socket_ready &&
      !(IsUser(cptr) && !IsOper(cptr) &&
	DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) {
    switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) {
    case IO_SUCCESS:
      if (length)
      {
        cli_lasttime(cptr) = CurrentTime;
        ClearPingSent(cptr);
        ClrFlag(cptr, FLAG_NONL);
        if (cli_lasttime(cptr) > cli_since(cptr))
          cli_since(cptr) = cli_lasttime(cptr);
      }
      break;
    case IO_BLOCKED:
      break;
    case IO_FAILURE:
      cli_error(cptr) = errno;
      /* SetFlag(cptr, FLAG_DEADSOCKET); */
      return 0;
    }
  }

  /*
   * For server connections, we process as many as we can without
   * worrying about the time of day or anything :)
   */
  if (length > 0 && IsServer(cptr))
    return server_dopacket(cptr, readbuf, length);
  else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr)))
    return connect_dopacket(cptr, readbuf, length);
  else
  {
    /*
     * Before we even think of parsing what we just read, stick
     * it on the end of the receive queue and do it when its
     * turn comes around.
     */
    if (length > 0 && dbuf_put(&(cli_recvQ(cptr)), readbuf, length) == 0)
      return exit_client(cptr, cptr, &me, "dbuf_put fail");

    if (IsUser(cptr)) {
      if (DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD)
	&& !IsOper(cptr))
      return exit_client(cptr, cptr, &me, "Excess Flood");
    }

    while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && 
           (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10))
    {
      dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE);
      /*
       * Devious looking...whats it do ? well..if a client
       * sends a *long* message without any CR or LF, then
       * dbuf_getmsg fails and we pull it out using this
       * loop which just gets the next 512 bytes and then
       * deletes the rest of the buffer contents.
       * -avalon
       */
      if (dolen == 0)
      {
        if (DBufLength(&(cli_recvQ(cptr))) < 510)
          SetFlag(cptr, FLAG_NONL);
        else
        {
          /* More than 512 bytes in the line - drop the input and yell
           * at the client.
           */
          DBufClear(&(cli_recvQ(cptr)));
          send_reply(cptr, ERR_INPUTTOOLONG);
        }
      }
      else if (client_dopacket(cptr, dolen) == CPTR_KILLED)
        return CPTR_KILLED;
      /*
       * If it has become registered as a Server
       * then skip the per-message parsing below.
       */
      if (IsHandshake(cptr) || IsServer(cptr))
      {
        while (-1)
        {
          dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf));
          if (dolen <= 0)
            return 1;
          else if (dolen == 0)
          {
            if (DBufLength(&(cli_recvQ(cptr))) < 510)
              SetFlag(cptr, FLAG_NONL);
            else
              DBufClear(&(cli_recvQ(cptr)));
          }
          else if ((IsServer(cptr) &&
                    server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) ||
                   (!IsServer(cptr) &&
                    connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED))
            return CPTR_KILLED;
        }
      }
    }

    /* If there's still data to process, wait 2 seconds first */
    if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) &&
	!t_onqueue(&(cli_proc(cptr))))
    {
      Debug((DEBUG_LIST, "Adding client process timer for %C", cptr));
      cli_freeflag(cptr) |= FREEFLAG_TIMER;
      timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr),
		TT_RELATIVE, 2);
    }
  }
  return 1;
}
Ejemplo n.º 11
0
/** Close the physical connection.  Side effects: MyConnect(cptr)
 * becomes false and cptr->from becomes NULL.
 * @param cptr Client to disconnect.
 */
void close_connection(struct Client *cptr)
{
  struct ConfItem* aconf;

  if (IsServer(cptr)) {
    ServerStats->is_sv++;
    ServerStats->is_sbs += cli_sendB(cptr);
    ServerStats->is_sbr += cli_receiveB(cptr);
    ServerStats->is_sti += CurrentTime - cli_firsttime(cptr);
    /*
     * If the connection has been up for a long amount of time, schedule
     * a 'quick' reconnect, else reset the next-connect cycle.
     */
    if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) {
      /*
       * Reschedule a faster reconnect, if this was a automatically
       * connected configuration entry. (Note that if we have had
       * a rehash in between, the status has been changed to
       * CONF_ILLEGAL). But only do this if it was a "good" link.
       */
      aconf->hold = CurrentTime;
      aconf->hold += ((aconf->hold - cli_since(cptr) >
		       feature_int(FEAT_HANGONGOODLINK)) ?
		      feature_int(FEAT_HANGONRETRYDELAY) : ConfConFreq(aconf));
/*        if (nextconnect > aconf->hold) */
/*          nextconnect = aconf->hold; */
    }
  }
  else if (IsUser(cptr)) {
    ServerStats->is_cl++;
    ServerStats->is_cbs += cli_sendB(cptr);
    ServerStats->is_cbr += cli_receiveB(cptr);
    ServerStats->is_cti += CurrentTime - cli_firsttime(cptr);
  }
  else
    ServerStats->is_ni++;

  if (-1 < cli_fd(cptr)) {
    flush_connections(cptr);
    LocalClientArray[cli_fd(cptr)] = 0;
    close(cli_fd(cptr));
    socket_del(&(cli_socket(cptr))); /* queue a socket delete */
    cli_fd(cptr) = -1;
  }
  SetFlag(cptr, FLAG_DEADSOCKET);

  MsgQClear(&(cli_sendQ(cptr)));
  client_drop_sendq(cli_connect(cptr));
  DBufClear(&(cli_recvQ(cptr)));
  memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr)));
  set_snomask(cptr, 0, SNO_SET);

  det_confs_butmask(cptr, 0);

  if (cli_listener(cptr)) {
    release_listener(cli_listener(cptr));
    cli_listener(cptr) = 0;
  }

  for ( ; HighestFd > 0; --HighestFd) {
    if (LocalClientArray[HighestFd])
      break;
  }
}