Пример #1
0
/*
 * m_pass() - Added Sat, 4 March 1989
 *
 *
 * m_pass - PASS message handler
 *      parv[0] = sender prefix
 *      parv[1] = password
 *      parv[2] = optional extra version information
 */
int m_pass(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  char* password = parc > 1 ? parv[1] : NULL;

  if (EmptyString(password))
    {
      sendto_one(cptr, form_str(ERR_NEEDMOREPARAMS),
                 me.name, parv[0], "PASS");
      return 0;
    }
  if (!MyConnect(sptr) || (!IsUnknown(cptr) && !IsHandshake(cptr)))
    {
      sendto_one(cptr, form_str(ERR_ALREADYREGISTRED),
                 me.name, parv[0]);
      return 0;
    }
  strncpy_irc(cptr->passwd, password, PASSWDLEN);
  if (parc > 2)
    {
      /* 
       * It looks to me as if orabidoo wanted to have more
       * than one set of option strings possible here...
       * i.e. ":AABBTS" as long as TS was the last two chars
       * however, as we are now using CAPAB, I think we can
       * safely assume if there is a ":TS" then its a TS server
       * -Dianora
       */
      if ((0 == irccmp(parv[2], "TS")) && (cptr->tsinfo == 0))
        cptr->tsinfo = TS_DOESTS;
    }
  return 0;
}
Пример #2
0
/*
 * mr_error - unregistered client message handler
 *
 * parv[0] = sender prefix
 * parv[parc-1] = text
 */
int mr_error(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  const char *para;

  if (!IsHandshake(cptr) && !IsConnecting(cptr))
    return 0; /* ignore ERROR from regular clients */

  para = (parc > 1 && *parv[parc - 1] != '\0') ? parv[parc - 1] : "<>";

  Debug((DEBUG_ERROR, "Received ERROR message from %s: %s", cli_name(sptr), para));

  if (cptr == sptr)
    sendto_opmask_butone(0, SNO_OLDSNO, "ERROR :from %C -- %s", cptr, para);
  else
    sendto_opmask_butone(0, SNO_OLDSNO, "ERROR :from %C via %C -- %s", sptr,
			 cptr, para);

  if (cli_serv(sptr))
  {
    MyFree(cli_serv(sptr)->last_error_msg);
    DupString(cli_serv(sptr)->last_error_msg, para);
  }

  return 0;
}
Пример #3
0
/*
 * m_close - CLOSE message handler
 *  - added by Darren Reed Jul 13 1992.
 */
int m_close(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Client* acptr;
  int            i;
  int            closed = 0;

  if (!HasUmode(sptr,UMODE_KILL))
    {
      sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]);
      return 0;
    }

  for (i = highest_fd; i; i--)
    {
      if (!(acptr = local[i]))
        continue;
      if (!IsUnknown(acptr) && !IsConnecting(acptr) &&
          !IsHandshake(acptr))
        continue;
      sendto_one(sptr, form_str(RPL_CLOSING), me.name, parv[0],
                 get_client_name(acptr, SHOW_IP), acptr->status);
      (void)exit_client(acptr, acptr, acptr, "Oper Closing");
      closed++;
    }
  sendto_one(sptr, form_str(RPL_CLOSEEND), me.name, parv[0], closed);
  return 0;
}
Пример #4
0
void report_fds(aClient *cptr)
{
   int i;
   char *name, *blocking;

   for(i = 0; i < MAXCONNECTIONS; i++)
   {
      if(fd_list[i].type == FDT_NONE)
         continue;

      if(fd_list[i].type == FDT_CLIENT ||
         fd_list[i].type == FDT_AUTH)
      {
         aClient *cptr = (aClient *) fd_list[i].value;
         int hide = (IsConnecting(cptr) || IsHandshake(cptr) || IsServer(cptr)) ? HIDEME : 0;

         name = get_client_name(cptr, hide);
         blocking = (cptr->flags & FLAGS_BLOCKED) ?
                    "BLOCKED" : "_";
      }
      else
      {
         name = "-";
         blocking = "-";
      }

    sendto_one(&me, cptr, ":%s %d %s :%d - %s [%s] %s %s",
                 me.name, RPL_STATSDEBUG, cptr->name,
                 i, type_string(fd_list[i].type),
                 flags_string(fd_list[i].flags),
                 name, blocking);

   }
}
Пример #5
0
/** 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;

  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_butone(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_butone(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr));
    return 0;
  }
  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);

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

  return (IsDead(cptr)) ? 0 : 1;
}
Пример #6
0
/*
 * Attempt to send a sequence of bytes to the connection.
 * Returns
 *
 * < 0	Some fatal error occurred, (but not EWOULDBLOCK).
 * 	This return is a request to close the socket and
 * 	clean up the link.
 * 
 * >= 0	No real error occurred, returns the number of
 * 	bytes actually transferred. EWOULDBLOCK and other
 * 	possibly similar conditions should be mapped to
 * 	zero return. Upper level routine will have to
 * 	decide what to do with those unwritten bytes...
 *
 * *NOTE*	alarm calls have been preserved, so this should
 * 	work equally well whether blocking or non-blocking
 * 	mode is used...
 */
int
deliver_it(aClient *cptr, char *str, int len)
{
	int retval;
	aClient	*acpt = cptr->acpt;

#ifdef	DEBUGMODE
	writecalls++;
#endif
	if (IsDead(cptr)
	    || (!IsServer(cptr) && !IsPerson(cptr) &&
		!IsHandshake(cptr) && !IsUnknown(cptr))) {
		str[len]='\0';
		sendto_ops("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s",
			   cptr->name, cptr->status,
			   IsDead(cptr)?"DEAD":"", str);
		return -1;
	}

	retval = socket_write(cptr->sock, str, len);
	/*
	 * Convert WOULDBLOCK to a return of "0 bytes moved". This
	 * should occur only if socket was non-blocking. Note, that
	 * all is Ok, if the 'write' just returns '0' instead of an
	 * error and errno=EWOULDBLOCK.
	 */

#ifdef DEBUGMODE
	if (retval < 0) {
		writeb[0]++;
               Debug((DEBUG_ERROR,"write error (%s) to %s",
		      strerror(errno), cptr->name));

	} else if (retval == 0)
		writeb[1]++;
	else if (retval < 16)
		writeb[2]++;
	else if (retval < 32)
		writeb[3]++;
	else if (retval < 64)
		writeb[4]++;
	else if (retval < 128)
		writeb[5]++;
	else if (retval < 256)
		writeb[6]++;
	else if (retval < 512)
		writeb[7]++;
	else if (retval < 1024)
		writeb[8]++;
	else
		writeb[9]++;
#endif
	if (retval > 0) {
		cptr->sendB += retval;
		me.sendB += retval;
		if (cptr->sendB > 1023) {
			cptr->sendK += (cptr->sendB >> 10);
			cptr->sendB &= 0x03ff;	/* 2^10 = 1024, 3ff = 1023 */
		}
Пример #7
0
/* handle_command()
 *
 * inputs	- pointer to message block
 *		- pointer to client
 *		- pointer to client message is from
 *		- count of number of args
 *		- pointer to argv[] array
 * output	- -1 if error from server
 * side effects	-
 */
static void
handle_command(struct Message *mptr, struct Client *client_p,
               struct Client *from, unsigned int i, char *hpara[MAXPARA])
{
  MessageHandler handler = 0;

  if (IsServer(client_p))
    mptr->rcount++;

  mptr->count++;

  /* New patch to avoid server flooding from unregistered connects
   * - Pie-Man 07/27/2000 */
  if (!IsRegistered(client_p))
  {
    /* if its from a possible server connection
     * ignore it.. more than likely its a header thats sneaked through
     */
    if ((IsHandshake(client_p) || IsConnecting(client_p) ||
        IsServer(client_p)) && !(mptr->flags & MFLG_UNREG))
      return;
  }

  handler = mptr->handlers[client_p->handler];

  /* check right amount of params is passed... --is */
  if (i < mptr->parameters)
  {
    if (!IsServer(client_p))
    {
      sendto_one(client_p, form_str(ERR_NEEDMOREPARAMS),
                   me.name, EmptyString(hpara[0]) ? "*" : hpara[0], mptr->cmd);
    }
    else
    {
      sendto_gnotice_flags(UMODE_ALL, L_ALL, me.name, &me, NULL,
                           "Dropping server %s due to (invalid) command '%s' "
                           "with only %d arguments (expecting %d).",
                           client_p->name, mptr->cmd, i, mptr->parameters);
      ilog(L_CRIT, "Insufficient parameters (%d) for command '%s' from %s.",
           i, mptr->cmd, client_p->name);
      exit_client(client_p, client_p,
                  "Not enough arguments to server command.");
    }
  }
  else
    (*handler)(client_p, from, i, hpara);
}
Пример #8
0
/*
 * Note: At least at protocol level ERROR has only one parameter.
 * --msa
 *
 *      parv[0] = command
 *      parv[*] = parameters
 */
static int
mr_error(struct Client *source_p, int parc, char *parv[])
{
  const char *para = (parc > 1 && !EmptyString(parv[1])) ? parv[1] : "<>";

  assert(MyConnect(source_p));

  if (!IsHandshake(source_p) && !IsConnecting(source_p))
    return 0;

  ilog(LOG_TYPE_IRCD, "Received ERROR message from %s: %s",
       source_p->name, para);

  sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE,
                       "ERROR :from %s -- %s",
                       get_client_name(source_p, HIDE_IP), para);
  sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE,
                       "ERROR :from %s -- %s",
                       get_client_name(source_p, MASK_IP), para);
  return 0;
}
/** Update server start timestamps and TS offsets.
 * @param[in] cptr Server that just connected.
 * @param[in] timestamp Current time according to \a cptr.
 * @param[in] start_timestamp Time that \a cptr started.
 * @param[in] recv_time Current time as we know it.
 */
static void
check_start_timestamp(struct Client *cptr, time_t timestamp, time_t start_timestamp, time_t recv_time)
{
  Debug((DEBUG_DEBUG, "My start time: %Tu; other's start time: %Tu",
         cli_serv(&me)->timestamp, start_timestamp));
  Debug((DEBUG_DEBUG, "Receive time: %Tu; received timestamp: %Tu; "
         "difference %ld", recv_time, timestamp, timestamp - recv_time));
  if (feature_bool(FEAT_RELIABLE_CLOCK)) {
    if (start_timestamp < cli_serv(&me)->timestamp)
      cli_serv(&me)->timestamp = start_timestamp;
    if (IsUnknown(cptr))
      cli_serv(cptr)->timestamp = TStime();
  } else if (start_timestamp < cli_serv(&me)->timestamp) {
    sendto_opmask_butone(0, SNO_OLDSNO, "got earlier start time: "
                         "%Tu < %Tu", start_timestamp,
                         cli_serv(&me)->timestamp);
    cli_serv(&me)->timestamp = start_timestamp;
    TSoffset += timestamp - recv_time;
    sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
                         (int)(timestamp - recv_time));
  } else if ((start_timestamp > cli_serv(&me)->timestamp) &&
             IsUnknown(cptr)) {
    cli_serv(cptr)->timestamp = TStime();
  } else if (timestamp != recv_time) {
    /*
     * Equal start times, we have a collision.  Let the connected-to
     * server decide. This assumes leafs issue more than half of the
     * connection attempts.
     */
    if (IsUnknown(cptr))
      cli_serv(cptr)->timestamp = TStime();
    else if (IsHandshake(cptr)) {
      sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
                           (int)(timestamp - recv_time));
      TSoffset += timestamp - recv_time;
    }
  }
}
Пример #10
0
int m_stats(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Message* mptr;
  struct Client*  acptr;
  char            l_stat = parc > 1 ? parv[1][0] : '\0';
  int             i;
  int             doall = 0;
  int             wilds = 0;
  int             ignore_request = 0;
  int             valid_stats = 0;
  char*           name;
  char *mename = me.name;
  
  if(sptr->user && sptr->user->vlink)
    mename = sptr->user->vlink->name;

  if(!IsClient(sptr))
    {
      return 0;
    }

  if(!IsAnOper(sptr))
    {
      sendto_one(sptr, form_str(ERR_NOPRIVILEGES), mename, parv[0]);
	  return -1;
    }

  if (hunt_server(cptr,sptr,":%s STATS %s :%s",2,parc,parv)!=HUNTED_ISME)
    return 0;

  if (parc > 2)
    {
      name = parv[2];
      if (!irccmp(name, mename))
        doall = 2;
      else if (match(name, mename))
        doall = 1;
      if (strchr(name, '*') || strchr(name, '?'))
        wilds = 1;
    }
  else
    name = mename;

  switch (l_stat)
    {
    case 'L' : case 'l' :
      /*
       * send info about connections which match, or all if the
       * mask matches mename.  Only restrictions are on those who
       * are invisible not being visible to 'foreigners' who use
       * a wild card based search to list it.
       */
      for (i = 0; i <= highest_fd; i++)
        {
          if (!(acptr = local[i]))
            continue;

			
          if (!doall && wilds && !match(name, acptr->name))
            continue;
          if (!(doall || wilds) && irccmp(name, acptr->name))
            continue;

          /* I've added a sanity test to the "CurrentTime - acptr->since"
           * occasionally, acptr->since is larger than CurrentTime.
           * The code in parse.c "randomly" increases the "since",
           * which means acptr->since is larger then CurrentTime at times,
           * this gives us very high odd number.. 
           * So, I am going to return 0 for ->since if this happens.
           * - Dianora
           */
          /* trust opers not on this server */
          /* if(IsAnOper(sptr)) */

          /* Don't trust opers not on this server */
#ifdef HIDE_SERVERS_IPS
          if(MyClient(sptr) && IsAnOper(sptr) && !IsServer(acptr) &&
	    !IsConnecting(acptr) && !IsHandshake(acptr)) 
#else
          if(MyClient(sptr) && IsAnOper(sptr))
#endif
            {
              sendto_one(sptr, Lformat, mename,
                     RPL_STATSLINKINFO, parv[0],
                     get_client_name(acptr, TRUE),
                     (int)DBufLength(&acptr->sendQ),
                     (int)acptr->sendM, (int)acptr->sendK,
                     (int)acptr->receiveM, (int)acptr->receiveK,
                     CurrentTime - acptr->firsttime,
                     (CurrentTime > acptr->since) ? (CurrentTime - acptr->since):0,
                     IsServer(acptr) ? show_capabilities(acptr) : "-");
              }
            else
              {
                if(IsServer(acptr))
                  sendto_one(sptr, Lformat, mename,
                     RPL_STATSLINKINFO, parv[0],
                     get_client_name(acptr, HIDEME),
                     (int)DBufLength(&acptr->sendQ),
                     (int)acptr->sendM, (int)acptr->sendK,
                     (int)acptr->receiveM, (int)acptr->receiveK,
                     CurrentTime - acptr->firsttime,
                     (CurrentTime > acptr->since) ? (CurrentTime - acptr->since):0,
                     IsServer(acptr) ? show_capabilities(acptr) : "-");
                 else
                  sendto_one(sptr, Lformat, mename,
                     RPL_STATSLINKINFO, parv[0],
                     (IsUpper(lstat)) ?
                     get_client_name(acptr, TRUE) :
                     get_client_name(acptr, FALSE),
                     (int)DBufLength(&acptr->sendQ),
                     (int)acptr->sendM, (int)acptr->sendK,
                     (int)acptr->receiveM, (int)acptr->receiveK,
                     CurrentTime - acptr->firsttime,
                     (CurrentTime > acptr->since) ? (CurrentTime - acptr->since):0,
                     IsServer(acptr) ? show_capabilities(acptr) : "-");
              }
        }
      valid_stats++;
      break;
    case 'C' : case 'c' :
      report_configured_links(sptr, CONF_CONNECT_SERVER|CONF_NOCONNECT_SERVER);
      valid_stats++;
      break;

    case 'B' : case 'b' :
      sendto_one(sptr,":%s NOTICE %s :Use stats I instead", mename, parv[0]);
      break;

    case 'D':
      report_dlines(sptr);
      valid_stats++;
      break;
      
    case 'd':
      report_domain_stats(sptr);
      valid_stats++;
      break;

    case 'E' : case 'e' :
      sendto_one(sptr,":%s NOTICE %s :Use stats I instead", mename, parv[0]);
      break;

    case 'F' : case 'f' :
      report_channel_stats(sptr);
      break;

    case 'g' :
      report_sxlines(sptr);
      valid_stats++;
      break;


    case 'G' :
      report_glines(sptr, NULL);
      valid_stats++;
      break;

    case 'H' : case 'h' :
      report_configured_links(sptr, CONF_HUB|CONF_LEAF);
      valid_stats++;
      break;

    case 'i' :
      report_vlinks(sptr);
      valid_stats++;
      break;

    case 'I' :
      report_mtrie_conf_links(sptr, CONF_CLIENT);
      valid_stats++;
      break;

    case 'k' :
      report_temp_klines(sptr);
      valid_stats++;
      break;

    case 'K' :
      if(parc > 3)
        report_matching_host_klines(sptr,parv[3]);
      else
        report_mtrie_conf_links(sptr, CONF_KILL);
      valid_stats++;
      break;

    case 'M' : case 'm' :
      for (mptr = msgtab; mptr->cmd; mptr++)
          sendto_one(sptr, form_str(RPL_STATSCOMMANDS),
                     mename, parv[0], mptr->cmd,
                     mptr->count, mptr->bytes);
      valid_stats++;
      break;

    case 'o' : case 'O' :
      report_configured_links(sptr, CONF_OPS);
      valid_stats++;
      break;

    case 'P' :
      show_ports(sptr);
      valid_stats++;
      break;

    case 'p' :
      show_opers(sptr);
      valid_stats++;
      break;

    case 'Q' :
      report_qlines(sptr);
      valid_stats++;
      break;

    case 'q' :
      report_sqlines(sptr);
      valid_stats++;
      break;	  

    case 'R' : case 'r' :
      send_usage(sptr,parv[0]);
      valid_stats++;
      break;

    case 'S' :
      list_scache(cptr,sptr,parc,parv);
      valid_stats++;
      break;

    case 'T' : case 't' :
      tstats(sptr, parv[0]);
      valid_stats++;
      break;

    case 'U' :
      report_specials(sptr,CONF_ULINE,RPL_STATSULINE);
      valid_stats++;
      break;

    case 'u' :
      {
        time_t now;
        
        now = CurrentTime - me.since;
        sendto_one(sptr, form_str(RPL_STATSUPTIME), mename, parv[0],
                   now/86400, (now/3600)%24, (now/60)%60, now%60);
        sendto_one(sptr, form_str(RPL_STATSCONN), mename, parv[0],
                   MaxConnectionCount, MaxClientCount,
                   Count.totalrestartcount);
        valid_stats++;
        break;
      }

    case 'V' :
      show_servers(sptr);
      valid_stats++;
      break;

    case 'v' :
      report_svlines(sptr);
      valid_stats++;
      break;
    case 'x' :
        report_specials(sptr,CONF_XLINE,RPL_STATSXLINE);
        valid_stats++;
      break;
	  
    case 'X' :        
	report_configured_links(sptr, CONF_MISSING);
        valid_stats++;
      break;;
	  

    case 'Y' : case 'y' :
        report_classes(sptr);
        valid_stats++;
      break;
	  
    case 'Z' :
      report_zlines(sptr);
      valid_stats++;
      break;

	case 'z' :
      count_memory(sptr, parv[0]);
      valid_stats++;
      break;

    case '?':
      serv_info(sptr, parv[0]);
      valid_stats++;
      break;

    default :
      l_stat = '*';
      break;
    }
  sendto_one(sptr, form_str(RPL_ENDOFSTATS), mename, parv[0], l_stat);

  /* personally, I don't see why opers need to see stats requests
   * at all. They are just "noise" to an oper, and users can't do
   * any damage with stats requests now anyway. So, why show them?
   * -Dianora
   */

  if (valid_stats)
    {
      if ( (l_stat == 'L') || (l_stat == 'l') )
        {
          sendto_ops_imodes(IMODE_SPY,
                "STATS %c requested by %s (%s@%s) [%s] on %s%s",
                l_stat,
                sptr->name,
                sptr->username,
                sptr->host,
                sptr->user->server,
                parc > 2 ? parv[2] : "<no recipient>",
                ignore_request > 0 ? " [request ignored]" : "\0"
                );
        }
      else
        {
          sendto_ops_imodes(IMODE_SPY,
                "STATS %c requested by %s (%s@%s) [%s]%s",
                l_stat,
                sptr->name,
                sptr->username,
                sptr->host,
                sptr->user->server,
                ignore_request > 0 ? " [request ignored]" : "\0"
                );
        }
    }

  return 0;
}
Пример #11
0
/*
 *  m_server
 *       parv[0] = sender prefix
 *       parv[1] = servername
 *       parv[2] = serverinfo/hopcount
 *       parv[3] = serverinfo
 */
int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int     i;
    char        info[REALLEN + 1], *host;
    aClient    *acptr, *bcptr;
    aConnect   *aconn;
    int         hop;
    char        nbuf[HOSTLEN * 2 + USERLEN + 5]; /* same size as in s_misc.c */

    info[0] = '\0';

    if (parc < 2 || *parv[1] == '\0')
    {
        sendto_one(cptr, "ERROR :No servername");
        return 0;
    }

    hop = 0;
    host = parv[1];
    if (parc > 3 && atoi(parv[2]))
    {
        hop = atoi(parv[2]);
        strncpyzt(info, parv[3], REALLEN + 1);
    }
    else if (parc > 2)
    {
        strncpyzt(info, parv[2], REALLEN + 1);
        if ((parc > 3) && ((i = strlen(info)) < (REALLEN - 2)))
        {
            strcat(info, " ");
            strncat(info, parv[3], REALLEN - i - 2);
            info[REALLEN] = '\0';
        }
    }
    /*
     * July 5, 1997
     * Rewritten to throw away server cruft from users,
     * combined the hostname validity test with cleanup of host name,
     * so a cleaned up hostname can be returned as an error if
     * necessary. - Dianora
     */

    /* yes, the if(strlen) below is really needed!! */
    if (strlen(host) > HOSTLEN)
        host[HOSTLEN] = '\0';

    if (IsPerson(cptr))
    {
        /* A local link that has been identified as a USER tries
         * something fishy... ;-)
         */
        sendto_one(cptr, err_str(ERR_UNKNOWNCOMMAND),
                   me.name, parv[0], "SERVER");

        return 0;
    }
    else
        /* hostile servername check */
    {
        /*
         * Lets check for bogus names and clean them up we don't bother
         * cleaning up ones from users, becasuse we will never see them
         * any more - Dianora
         */

        int bogus_server = 0;
        int found_dot = 0;
        char clean_host[(2 * HOSTLEN) + 1];
        char *s;
        char *d;
        int n;

        s = host;
        d = clean_host;
        n = (2 * HOSTLEN) - 2;

        while (*s && n > 0)
        {
            if ((unsigned char) *s < (unsigned char) ' ')
                /* Is it a control character? */
            {
                bogus_server = 1;
                *d++ = '^';
                *d++ = (char) ((unsigned char) *s + 0x40);
                /* turn it into a printable */
                n -= 2;
            }
            else if ((unsigned char) *s > (unsigned char) '~')
            {
                bogus_server = 1;
                *d++ = '.';
                n--;
            }
            else
            {
                if (*s == '.')
                    found_dot = 1;
                *d++ = *s;
                n--;
            }
            s++;
        }
        *d = '\0';

        if ((!found_dot) || bogus_server)
        {
            sendto_one(sptr, "ERROR :Bogus server name (%s)",
                       clean_host);
            return exit_client(cptr, cptr, cptr, "Bogus server name");
        }
    }

    /* new connection */
    if (IsUnknown(cptr) || IsHandshake(cptr))
    {
        strncpyzt(cptr->name, host, sizeof(cptr->name));
        strncpyzt(cptr->info, info[0] ? info : me.name, REALLEN + 1);
        cptr->hopcount = hop;

        switch (check_server_init(cptr))
        {
            case 0:
                return m_server_estab(cptr);
            case 1:
                sendto_ops("Access check for %s in progress",
                           get_client_name(cptr, HIDEME));
                return 1;
            default:
                ircstp->is_ref++;
                sendto_ops_lev(ADMIN_LEV, "Link %s dropped, no Connect block",
                           get_client_name(cptr, TRUE));
                return exit_client(cptr, cptr, cptr, "No Connect block");
        }
    }
    
    /* already linked server */
    if (!IsServer(cptr))
        return 0;

    if ((acptr = find_name(host, NULL)))
    {
        /*
         * * This link is trying feed me a server that I already have
         * access through another path -- multiple paths not accepted
         * currently, kill this link immediately!!
         *
         * Rather than KILL the link which introduced it, KILL the
         * youngest of the two links. -avalon
         */

        bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr :
            acptr->from;
        sendto_one(bcptr, "ERROR :Server %s already exists", host);
        if (bcptr == cptr)
        {
            /* Don't complain for servers that are juped */
            /* (don't complain if the server that already exists is U: lined,
                unless I actually have a .conf U: line for it */
            if(!IsULine(acptr) || find_aUserver(acptr->name))
            {
                sendto_gnotice("from %s: Link %s cancelled, server %s already "
                               "exists", me.name, get_client_name(bcptr, HIDEME),
                               host);
                sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, "
                                   "server %s already exists", me.name,
                                   get_client_name(bcptr, HIDEME), host);
            }
            return exit_client(bcptr, bcptr, &me, "Server Exists");
        }
        /* inform all those who care (set +n) -epi */
        strcpy(nbuf, get_client_name(bcptr, HIDEME));
        sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced "
                       "by %s", me.name, nbuf, host,
                       get_client_name(cptr, HIDEME));
        sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s "
                           "reintroduced by %s", me.name, nbuf, host,
                           get_client_name(cptr, HIDEME));
        exit_client(bcptr, bcptr, &me, "Server Exists");
    }
    /*
     * The following if statement would be nice to remove since user
     * nicks never have '.' in them and servers must always have '.' in
     * them. There should never be a server/nick name collision, but it
     * is possible a capricious server admin could deliberately do
     * something strange.
     *
     * -Dianora
     */

    if ((acptr = find_client(host, NULL)) && acptr != cptr)
    {
        /*
         * * Server trying to use the same name as a person. Would
         * cause a fair bit of confusion. Enough to make it hellish for
         * a while and servers to send stuff to the wrong place.
         */
        sendto_one(cptr, "ERROR :Nickname %s already exists!", host);
        strcpy(nbuf, get_client_name(cptr, HIDEME));
        sendto_gnotice("from %s: Link %s cancelled, servername/nick collision",
                       me.name, nbuf);
        sendto_serv_butone(cptr, ":%s GNOTICE :Link %s cancelled, "
                           "servername/nick collision", me.name, nbuf);
        return exit_client(cptr, cptr, cptr, "Nick as Server");
    }

    if (IsServer(cptr))
    {
        /*
         * * Server is informing about a new server behind this link.
         * Create REMOTE server structure, add it to list and propagate
         * word to my other server links...
         */
        if (parc == 1 || info[0] == '\0')
        {
            sendto_one(cptr, "ERROR :No server info specified for %s", host);
            return 0;
        }
        /*
         * * See if the newly found server is behind a guaranteed leaf
         * (L-line). If so, close the link.
         *
         * Depreciated.  Kinda redundant with Hlines. -epi
         */
        if (!(cptr->serv->aconn->flags & CONN_HUB))
        {
            aconn = cptr->serv->aconn;
            sendto_gnotice("from %s: Non-Hub link %s introduced %s",
                           me.name, get_client_name(cptr, HIDEME), host);
            sendto_serv_butone(cptr,":%s GNOTICE :Non-Hub link %s introduced "
                               "%s", me.name, get_client_name(cptr, HIDEME),
                               host);
            sendto_one(cptr, "ERROR :You're not a hub (introducing %s)",
                       host);
            return exit_client(cptr, cptr, cptr, "Too many servers");
        }

        acptr = make_client(cptr, sptr);
        make_server(acptr);
        acptr->hopcount = hop;
        strncpyzt(acptr->name, host, sizeof(acptr->name));
        strncpyzt(acptr->info, info, REALLEN + 1);
        acptr->serv->up = find_or_add(parv[0]);

        fakelinkserver_update(acptr->name, acptr->info);
        SetServer(acptr);

        /*
         * if this server is behind a U-lined server, make it U-lined as
         * well. - lucas
         */

        if (IsULine(sptr) || find_aUserver(acptr->name))
        {
            acptr->flags |= FLAGS_ULINE;
            sendto_realops_lev(DEBUG_LEV, "%s introducing super server %s",
                               cptr->name, acptr->name);
        }

        Count.server++;

        add_client_to_list(acptr);
        add_to_client_hash_table(acptr->name, acptr);
        /*
         * Old sendto_serv_but_one() call removed because we now need
         * to send different names to different servers (domain name matching)
         */
        for (i = 0; i <= highest_fd; i++)
        {
            if (!(bcptr = local[i]) || !IsServer(bcptr) || bcptr == cptr ||
                IsMe(bcptr))
                continue;
            if (!(aconn = bcptr->serv->aconn))
            {
                sendto_gnotice("from %s: Lost Connect block for %s on %s."
                               " Closing", me.name,
                               get_client_name(cptr, HIDEME), host);
                sendto_serv_butone(cptr, ":%s GNOTICE :Lost Connect block for"
                                   " %s on %s. Closing", me.name,
                                   get_client_name(cptr, HIDEME), host);
                return exit_client(cptr, cptr, cptr, "Lost Connect block");
            }
            if (match(my_name_for_link(me.name, aconn), acptr->name) == 0)
                continue;
            sendto_one(bcptr, ":%s SERVER %s %d :%s",
                       parv[0], acptr->name, hop + 1, acptr->info);
        }
        return 0;
    }

    return 0;
}
Пример #12
0
/** Check access for a server given its name (passed in cptr struct).
 * Must check for all C/N lines which have a name which matches the
 * name given and a host which matches. A host alias which is the
 * same as the server name is also acceptable in the host field of a
 * C/N line.
 * @param cptr Peer server to check.
 * @return 0 if accepted, -1 if access denied.
 */
int conf_check_server(struct Client *cptr)
{
  struct ConfItem* c_conf = NULL;
  struct SLink*    lp;

  Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", 
        cli_name(cptr), cli_sockhost(cptr)));

  if (IsUnknown(cptr) && !attach_confs_byname(cptr, cli_name(cptr), CONF_SERVER)) {
    Debug((DEBUG_DNS, "No C/N lines for %s", cli_sockhost(cptr)));
    return -1;
  }
  lp = cli_confs(cptr);
  /*
   * We initiated this connection so the client should have a C and N
   * line already attached after passing through the connect_server()
   * function earlier.
   */
  if (IsConnecting(cptr) || IsHandshake(cptr)) {
    c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER);
    if (!c_conf) {
      sendto_opmask_butone(0, SNO_OLDSNO,
                           "Connect Error: lost Connect block for %s",
                           cli_name(cptr));
      det_confs_butmask(cptr, 0);
      return -1;
    }
  }

  /* Try finding the Connect block by DNS name and IP next. */
  if (!c_conf && !(c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER)))
        c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER);

  /*
   * Attach by IP# only if all other checks have failed.
   * It is quite possible to get here with the strange things that can
   * happen when using DNS in the way the irc server does. -avalon
   */
  if (!c_conf)
    c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER);
  /*
   * detach all conf lines that got attached by attach_confs()
   */
  det_confs_butmask(cptr, 0);
  /*
   * if no Connect block, then deny access
   */
  if (!c_conf) {
    Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s]",
          cli_name(cptr), cli_username(cptr), cli_sockhost(cptr)));
    return -1;
  }
  /*
   * attach the Connect block to the client structure for later use.
   */
  attach_conf(cptr, c_conf);

  if (!irc_in_addr_valid(&c_conf->address.addr))
    memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr));

  Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]",
         cli_name(cptr), cli_sockhost(cptr)));
  return 0;
}
Пример #13
0
static	time_t	check_pings(time_t currenttime)
{		
  register	aClient	*cptr;		/* current local cptr being examined */
  aConfItem 	*aconf = (aConfItem *)NULL;
  int		ping = 0;		/* ping time value from client */
  int		i;			/* used to index through fd/cptr's */
  time_t	oldest = 0;		/* next ping time */
  time_t	timeout;		/* found necessary ping time */
  char *reason;				/* pointer to reason string */
  int die_index=0;			/* index into list */
  char ping_time_out_buffer[64];	/* blech that should be a define */

					/* of dying clients */
  dying_clients[0] = (aClient *)NULL;	/* mark first one empty */

  /*
   * I re-wrote the way klines are handled. Instead of rescanning
   * the local[] array and calling exit_client() right away, I
   * mark the client thats dying by placing a pointer to its aClient
   * into dying_clients[]. When I have examined all in local[],
   * I then examine the dying_clients[] for aClient's to exit.
   * This saves the rescan on k-lines, also greatly simplifies the code,
   *
   * Jan 28, 1998
   * -Dianora
   */

   for (i = 0; i <= highest_fd; i++)
    {
      if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr))
	continue;		/* and go examine next fd/cptr */
      /*
      ** Note: No need to notify opers here. It's
      ** already done when "FLAGS_DEADSOCKET" is set.
      */
      if (cptr->flags & FLAGS_DEADSOCKET)
	{
	  /* N.B. EVERY single time dying_clients[] is set
	   * it must be followed by an immediate continue,
	   * to prevent this cptr from being marked again for exit.
	   * If you don't, you could cause exit_client() to be called twice
	   * for the same cptr. i.e. bad news
	   * -Dianora
	   */

	  dying_clients[die_index] = cptr;
	  dying_clients_reason[die_index++] =
	    ((cptr->flags & FLAGS_SENDQEX) ?
	     "SendQ exceeded" : "Dead socket");
	  dying_clients[die_index] = (aClient *)NULL;
	  continue;		/* and go examine next fd/cptr */
	}

      if (rehashed)
	{
	  if(dline_in_progress)
	    {
	      if(IsPerson(cptr))
		{
		  if( (aconf = find_dkill(cptr)) ) /* if there is a returned 
						      aConfItem then kill it */
		    {
		      sendto_ops("D-line active for %s",
				 get_client_name(cptr, FALSE));

		      dying_clients[die_index] = cptr;
#ifdef KLINE_WITH_REASON
		      reason = aconf->passwd ? aconf->passwd : "D-lined";
		      dying_clients_reason[die_index++] = reason;
#else
		      dying_clients_reason[die_index++] = "D-lined";
#endif
		      dying_clients[die_index] = (aClient *)NULL;
		      sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP),
				 me.name, cptr->name, reason);
		      continue;		/* and go examine next fd/cptr */
		    }
		}
	    }
	  else
	    {
	      if(IsPerson(cptr))
		{
#ifdef GLINES
		  if( (aconf = find_gkill(cptr)) )
		    {
		      sendto_ops("G-line active for %s",
				 get_client_name(cptr, FALSE));

		      dying_clients[die_index] = cptr;
#ifdef KLINE_WITH_REASON
		      reason = aconf->passwd ? aconf->passwd : "G-lined";
		      dying_clients_reason[die_index++] = reason;
#else
		      dying_clients_reason[die_index++] = "G-lined";
#endif
		      dying_clients[die_index] = (aClient *)NULL;
		      sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP),
				 me.name, cptr->name, reason);
		      continue;		/* and go examine next fd/cptr */
		    }
		  else
#endif
		  if((aconf = find_kill(cptr)))	/* if there is a returned
						   aConfItem.. then kill it */
		    {
		      sendto_ops("K-line active for %s",
				 get_client_name(cptr, FALSE));
		      dying_clients[die_index] = cptr;

#ifdef KLINE_WITH_REASON
#ifdef K_COMMENT_ONLY
		      reason = aconf->passwd ? aconf->passwd : "K-lined";
#else
		      reason = (BadPtr(aconf->passwd) || 
				!is_comment(aconf->passwd)) ?
			"K-lined" : aconf->passwd;
#endif
		      dying_clients_reason[die_index++] = reason;
#else
		      dying_clients_reason[die_index++] = "K-lined";
#endif
		      dying_clients[die_index] = (aClient *)NULL;
		      sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP),
				 me.name, cptr->name, reason);
		      continue;		/* and go examine next fd/cptr */
		    }
		}
	    }
	}

#ifdef IDLE_CHECK
      if (IsPerson(cptr))
	{
	  if( !IsElined(cptr) && ((timeofday - cptr->user->last) > idle_time))
	    {
	      aConfItem *aconf;

	      dying_clients[die_index] = cptr;
	      dying_clients_reason[die_index++] = "idle exceeder";
	      dying_clients[die_index] = (aClient *)NULL;

	      aconf = make_conf();
	      aconf->status = CONF_KILL;
	      DupString(aconf->host, cptr->user->host);
	      DupString(aconf->passwd, "idle exceeder" );
	      DupString(aconf->name, cptr->user->username);
	      aconf->port = 0;
	      aconf->hold = timeofday + 60;
	      add_temp_kline(aconf);
	      sendto_ops("Idle exceeder %s temp k-lining",
			 get_client_name(cptr,FALSE));
	      continue;		/* and go examine next fd/cptr */
	    }
	}
#endif

#ifdef REJECT_HOLD
      if (IsRejectHeld(cptr))
	{
	  if( timeofday > (cptr->firsttime + REJECT_HOLD_TIME) )
	    {
	      dying_clients[die_index] = cptr;
	      dying_clients_reason[die_index++] = "reject held client";
	      dying_clients[die_index] = (aClient *)NULL;
	      continue;		/* and go examine next fd/cptr */
	    }
	}
#endif

#if defined(R_LINES) && defined(R_LINES_OFTEN)
      /*
       * this is used for KILL lines with time restrictions
       * on them - send a message to the user being killed
       * first.
       * *** Moved up above  -taner ***
       *
       * Moved here, no more rflag -Dianora 
       */
      if (IsPerson(cptr) && find_restrict(cptr))
	{
	  sendto_ops("Restricting %s, closing link.",
		     get_client_name(cptr,FALSE));

	  dying_clients[die_index] = cptr;
	  dying_clients_reason[die_index++] = "you have been R-lined";
	  dying_clients[die_index] = (aClient *)NULL;
	  continue;			/* and go examine next fd/cptr */
	}
#endif

      if (!IsRegistered(cptr))
	ping = CONNECTTIMEOUT;
      else
	ping = get_client_ping(cptr);

      /*
       * Ok, so goto's are ugly and can be avoided here but this code
       * is already indented enough so I think its justified. -avalon
       */
       /*  if (!rflag &&
	       (ping >= currenttime - cptr->lasttime))
	      goto ping_timeout; */

      /*
       * *sigh* I think not -Dianora
       */

      if (ping < (currenttime - cptr->lasttime))
	{

	  /*
	   * If the server hasnt talked to us in 2*ping seconds
	   * and it has a ping time, then close its connection.
	   * If the client is a user and a KILL line was found
	   * to be active, close this connection too.
	   */
	  if (((currenttime - cptr->lasttime) >= (2 * ping) &&
	       (cptr->flags & FLAGS_PINGSENT)) ||
	      ((!IsRegistered(cptr) && (currenttime - cptr->since) >= ping)))
	    {
	      if (!IsRegistered(cptr) &&
		  (DoingDNS(cptr) || DoingAuth(cptr)))
		{
		  if (cptr->authfd >= 0)
		    {
		      (void)close(cptr->authfd);
		      cptr->authfd = -1;
		      cptr->count = 0;
		      *cptr->buffer = '\0';
		    }
#ifdef SHOW_HEADERS
		  if (DoingDNS(cptr))
		    send(cptr->fd, REPORT_FAIL_DNS, R_fail_dns, 0);
		  else
		    send(cptr->fd, REPORT_FAIL_ID, R_fail_id, 0);
#endif
		  Debug((DEBUG_NOTICE,"DNS/AUTH timeout %s",
			 get_client_name(cptr,TRUE)));
		  del_queries((char *)cptr);
		  ClearAuth(cptr);
		  ClearDNS(cptr);
		  SetAccess(cptr);
		  cptr->since = currenttime;
		  continue;
		}
	      if (IsServer(cptr) || IsConnecting(cptr) ||
		  IsHandshake(cptr))
		{
		  sendto_ops("No response from %s, closing link",
			     get_client_name(cptr, FALSE));
		}
	      /*
	       * this is used for KILL lines with time restrictions
	       * on them - send a messgae to the user being killed
	       * first.
	       * *** Moved up above  -taner ***
	       */
	      cptr->flags2 |= FLAGS2_PING_TIMEOUT;
	      dying_clients[die_index++] = cptr;
	      /* the reason is taken care of at exit time */
      /*      dying_clients_reason[die_index++] = "Ping timeout"; */
	      dying_clients[die_index] = (aClient *)NULL;
	      
	      /*
	       * need to start loop over because the close can
	       * affect the ordering of the local[] array.- avalon
	       *
	       ** Not if you do it right - Dianora
	       */

	      continue;
	    }
	  else if ((cptr->flags & FLAGS_PINGSENT) == 0)
	    {
	      /*
	       * if we havent PINGed the connection and we havent
	       * heard from it in a while, PING it to make sure
	       * it is still alive.
	       */
	      cptr->flags |= FLAGS_PINGSENT;
	      /* not nice but does the job */
	      cptr->lasttime = currenttime - ping;
	      sendto_one(cptr, "PING :%s", me.name);
	    }
	}
      /* ping_timeout: */
      timeout = cptr->lasttime + ping;
      while (timeout <= currenttime)
	timeout += ping;
      if (timeout < oldest || !oldest)
	oldest = timeout;

      /*
       * Check UNKNOWN connections - if they have been in this state
       * for > 100s, close them.
       */

      if (IsUnknown(cptr))
	{
	  if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0)
	    {
	      dying_clients[die_index] = cptr;
	      dying_clients_reason[die_index++] = "Connection Timed Out";
	      dying_clients[die_index] = (aClient *)NULL;
	      continue;
	    }
	}
    }

  /* Now exit clients marked for exit above.
   * it doesn't matter if local[] gets re-arranged now
   *
   * -Dianora
   */

  for(die_index = 0; (cptr = dying_clients[die_index]); die_index++)
    {
      if(cptr->flags2 & FLAGS2_PING_TIMEOUT)
	{
	  (void)ircsprintf(ping_time_out_buffer,
			    "Ping timeout: %d seconds",
			    currenttime - cptr->lasttime);
	  (void)exit_client(cptr, cptr, &me, ping_time_out_buffer );
	}
      else
	(void)exit_client(cptr, cptr, &me, dying_clients_reason[die_index]);
    }

  rehashed = 0;
  dline_in_progress = 0;

  if (!oldest || oldest < currenttime)
    oldest = currenttime + PINGFREQUENCY;
  Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d",
	 myctime(oldest), ping, oldest, currenttime));
  
  return (oldest);
}
Пример #14
0
/** Handle a connection that has sent a valid PASS and SERVER.
 * @param cptr New peer server.
 * @param aconf Connect block for \a cptr.
 * @return Zero.
 */
int server_estab(struct Client *cptr, struct ConfItem *aconf)
{
  struct Client* acptr = 0;
  const char*    inpath;
  int            i;

  assert(0 != cptr);
  assert(0 != cli_local(cptr));

  inpath = cli_name(cptr);

  if (IsUnknown(cptr)) {
    if (aconf->passwd[0])
      sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd);
    /*
     *  Pass my info to the new server
     */
    sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s",
		  cli_name(&me), cli_serv(&me)->timestamp,
		  cli_serv(cptr)->timestamp, MAJOR_PROTOCOL, NumServCap(&me),
		  feature_bool(FEAT_HUB) ? "h" : "",
		  *(cli_info(&me)) ? cli_info(&me) : "IRCers United");
  }

  det_confs_butmask(cptr, CONF_SERVER);

  if (!IsHandshake(cptr))
    hAddClient(cptr);
  SetServer(cptr);
  cli_handler(cptr) = SERVER_HANDLER;
  Count_unknownbecomesserver(UserStats);
  SetBurst(cptr);

/*    nextping = CurrentTime; */

  /*
   * NOTE: check for acptr->user == cptr->serv->user is necessary to insure
   * that we got the same one... bleah
   */
  if (cli_serv(cptr)->user && *(cli_serv(cptr))->by &&
      (acptr = findNUser(cli_serv(cptr)->by))) {
    if (cli_user(acptr) == cli_serv(cptr)->user) {
      sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s established.",
                    acptr, inpath);
    }
    else {
      /*
       * if not the same client, set by to empty string
       */
      acptr = 0;
      *(cli_serv(cptr))->by = '\0';
    }
  }

  sendto_opmask(acptr, SNO_OLDSNO, "Link with %s established.", inpath);
  cli_serv(cptr)->up = &me;
  cli_serv(cptr)->updown = add_dlink(&(cli_serv(&me))->down, cptr);
  sendto_opmask(0, SNO_NETWORK, "Net junction: %s %s", cli_name(&me),
                cli_name(cptr));
  SetJunction(cptr);
  /*
   * Old sendto_serv_but_one() call removed because we now
   * need to send different names to different servers
   * (domain name matching) Send new server to other servers.
   */
  for (i = 0; i <= HighestFd; i++)
  {
    if (!(acptr = LocalClientArray[i]) || !IsServer(acptr) ||
        acptr == cptr || IsMe(acptr))
      continue;
    if (!match(cli_name(&me), cli_name(cptr)))
      continue;
    sendcmdto_one(&me, CMD_SERVER, acptr,
		  "%s 2 0 %Tu J%02u %s%s +%s%s%s :%s", cli_name(cptr),
		  cli_serv(cptr)->timestamp, Protocol(cptr), NumServCap(cptr),
		  IsHub(cptr) ? "h" : "", IsService(cptr) ? "s" : "",
		  IsIPv6(cptr) ? "6" : "", cli_info(cptr));
  }

  /* Send these as early as possible so that glined users/juped servers can
   * be removed from the network while the remote server is still chewing
   * our burst.
   */
  gline_burst(cptr);
  jupe_burst(cptr);

  /*
   * Pass on my client information to the new server
   *
   * First, pass only servers (idea is that if the link gets
   * canceled because the server was already there,
   * there are no NICK's to be canceled...). Of course,
   * if cancellation occurs, all this info is sent anyway,
   * and I guess the link dies when a read is attempted...? --msa
   *
   * Note: Link cancellation to occur at this point means
   * that at least two servers from my fragment are building
   * up connection this other fragment at the same time, it's
   * a race condition, not the normal way of operation...
   */

  for (acptr = &me; acptr; acptr = cli_prev(acptr)) {
    /* acptr->from == acptr for acptr == cptr */
    if (cli_from(acptr) == cptr)
      continue;
    if (IsServer(acptr)) {
      const char* protocol_str;

      if (Protocol(acptr) > 9)
        protocol_str = IsBurst(acptr) ? "J" : "P";
      else
        protocol_str = IsBurst(acptr) ? "J0" : "P0";

      if (0 == match(cli_name(&me), cli_name(acptr)))
        continue;
      sendcmdto_one(cli_serv(acptr)->up, CMD_SERVER, cptr,
		    "%s %d 0 %Tu %s%u %s%s +%s%s%s :%s", cli_name(acptr),
		    cli_hopcount(acptr) + 1, cli_serv(acptr)->timestamp,
		    protocol_str, Protocol(acptr), NumServCap(acptr),
		    IsHub(acptr) ? "h" : "", IsService(acptr) ? "s" : "",
		    IsIPv6(acptr) ? "6" : "", cli_info(acptr));
    }
  }

  for (acptr = &me; acptr; acptr = cli_prev(acptr))
  {
    /* acptr->from == acptr for acptr == cptr */
    if (cli_from(acptr) == cptr)
      continue;
    if (IsUser(acptr))
    {
      char xxx_buf[25];
      char *s = umode_str(acptr);
      sendcmdto_one(cli_user(acptr)->server, CMD_NICK, cptr,
		    "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
		    cli_name(acptr), cli_hopcount(acptr) + 1, cli_lastnick(acptr),
		    cli_user(acptr)->username, cli_user(acptr)->realhost,
		    *s ? "+" : "", s, *s ? " " : "",
		    iptobase64(xxx_buf, &cli_ip(acptr), sizeof(xxx_buf), IsIPv6(cptr)),
		    NumNick(acptr), cli_info(acptr));
    }
  }
  /*
   * Last, send the BURST.
   * (Or for 2.9 servers: pass all channels plus statuses)
   */
  {
    struct Channel *chptr;
    for (chptr = GlobalChannelList; chptr; chptr = chptr->next)
      send_channel_modes(cptr, chptr);
  }
  sendcmdto_one(&me, CMD_END_OF_BURST, cptr, "");
  return 0;
}
Пример #15
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;
}
Пример #16
0
/*
 * Check_pings_list()
 *
 * inputs	- pointer to list to check
 * output	- NONE
 * side effects	- 
 */
static void
check_pings_list(dlink_list *list)
{
  char         scratch[32];	/* way too generous but... */
  struct Client *client_p;          /* current local client_p being examined */
  int           ping = 0;       /* ping time value from client */
  dlink_node    *ptr, *next_ptr;

  for (ptr = list->head; ptr; ptr = next_ptr)
    {
      next_ptr = ptr->next;
      client_p = ptr->data;

      /*
      ** Note: No need to notify opers here. It's
      ** already done when "FLAGS_DEADSOCKET" is set.
      */
      if (client_p->flags & FLAGS_DEADSOCKET)
        {
	  /* Ignore it, its been exited already */
          continue; 
        }
      if (IsPerson(client_p))
        {
          if(!IsExemptKline(client_p) &&
             GlobalSetOptions.idletime && 
             !IsOper(client_p) &&
             !IsIdlelined(client_p) && 
             ((CurrentTime - client_p->user->last) > GlobalSetOptions.idletime))
            {
              struct ConfItem *aconf;

              aconf = make_conf();
              aconf->status = CONF_KILL;

              DupString(aconf->host, client_p->host);
              DupString(aconf->passwd, "idle exceeder");
              DupString(aconf->name, client_p->username);
              aconf->port = 0;
              aconf->hold = CurrentTime + 60;
              add_temp_kline(aconf);
              sendto_realops_flags(FLAGS_ALL, L_ALL,
			   "Idle time limit exceeded for %s - temp k-lining",
				   get_client_name(client_p, HIDE_IP));


	      (void)exit_client(client_p, client_p, &me, aconf->passwd);
              continue;
            }
        }

      if (!IsRegistered(client_p))
        ping = CONNECTTIMEOUT;
      else
        ping = get_client_ping(client_p);

      if (ping < (CurrentTime - client_p->lasttime))
        {
          /*
           * If the client/server hasnt talked to us in 2*ping seconds
           * and it has a ping time, then close its connection.
           */
          if (((CurrentTime - client_p->lasttime) >= (2 * ping) &&
               (client_p->flags & FLAGS_PINGSENT)))
            {
              if (IsServer(client_p) || IsConnecting(client_p) ||
                  IsHandshake(client_p))
                {
                  sendto_realops_flags(FLAGS_ALL, L_ADMIN,
				       "No response from %s, closing link",
				       get_client_name(client_p, HIDE_IP));
                  sendto_realops_flags(FLAGS_ALL, L_OPER,
                                       "No response from %s, closing link",
                                       get_client_name(client_p, MASK_IP));
                  ilog(L_NOTICE, "No response from %s, closing link",
                      get_client_name(client_p, HIDE_IP));
                }
	      (void)ircsprintf(scratch,
			       "Ping timeout: %d seconds",
			       (int)(CurrentTime - client_p->lasttime));
	      
	      (void)exit_client(client_p, client_p, &me, scratch);
              continue;
            }
          else if ((client_p->flags & FLAGS_PINGSENT) == 0)
            {
              /*
               * if we havent PINGed the connection and we havent
               * heard from it in a while, PING it to make sure
               * it is still alive.
               */
              client_p->flags |= FLAGS_PINGSENT;
              /* not nice but does the job */
              client_p->lasttime = CurrentTime - ping;
              sendto_one(client_p, "PING :%s", me.name);
            }
        }
      /* ping_timeout: */

    }
}
Пример #17
0
/*
** exit_client - This is old "m_bye". Name  changed, because this is not a
**        protocol function, but a general server utility function.
**
**        This function exits a client of *any* type (user, server, etc)
**        from this server. Also, this generates all necessary prototol
**        messages that this exit may cause.
**
**   1) If the client is a local client, then this implicitly
**        exits all other clients depending on this connection (e.g.
**        remote clients having 'from'-field that points to this.
**
**   2) If the client is a remote client, then only this is exited.
**
** For convenience, this function returns a suitable value for
** m_function return value:
**
**        CLIENT_EXITED        if (client_p == source_p)
**        0                if (client_p != source_p)
*/
int exit_client(
                struct Client* client_p, /* The local client originating the
                                          * exit or NULL, if this exit is
                                          * generated by this server for
                                          * internal reasons.
                                          * This will not get any of the
                                          * generated messages. */
                struct Client* source_p, /* Client exiting */
                struct Client* from,     /* Client firing off this Exit,
                                          * never NULL! */
                const char* comment      /* Reason for the exit */
               )
{
  char comment1[HOSTLEN + HOSTLEN + 2];
  dlink_node *m;
  fprintf(stderr, "Exiting client: %s\n", comment);
  if (MyConnect(source_p))
    {
      /* DO NOT REMOVE. exit_client can be called twice after a failed
       * read/write.
       */
      if(IsClosing(source_p))
        return 0;

      SetClosing(source_p);
      if (source_p->flags & FLAGS_IPHASH)
        remove_one_ip(&source_p->localClient->ip);

      delete_adns_queries(source_p->localClient->dns_query);
      delete_identd_queries(source_p);
      client_flush_input(source_p);

      /* This source_p could have status of one of STAT_UNKNOWN, STAT_CONNECTING
       * STAT_HANDSHAKE or STAT_UNKNOWN
       * all of which are lumped together into unknown_list
       *
       * In all above cases IsRegistered() will not be true.
       */
      if (!IsRegistered(source_p))
	{
	  m = dlinkFind(&unknown_list,source_p);
	  if(m != NULL)
	    {
	      dlinkDelete(m, &unknown_list);
	      free_dlink_node(m);
	    }
	}
      if (IsOper(source_p))
        {
	  m = dlinkFind(&oper_list,source_p);
	  if(m != NULL)
	    {
	      dlinkDelete(m, &oper_list);
	      free_dlink_node(m);
	    }
        }
      if (IsClient(source_p))
        {
          Count.local--;

          if(IsPerson(source_p))        /* a little extra paranoia */
            {
	      m = dlinkFind(&lclient_list,source_p);
	      if(m != NULL)
		{
		  dlinkDelete(m,&lclient_list);
		  free_dlink_node(m);
		}
            }
        }

      /* As soon as a client is known to be a server of some sort
       * it has to be put on the serv_list, or SJOIN's to this new server
       * from the connect burst will not be seen.
       */
      if (IsServer(source_p) || IsConnecting(source_p) ||
          IsHandshake(source_p))
	{
	  m = dlinkFind(&serv_list,source_p);
	  if(m != NULL)
	    {
	      dlinkDelete(m,&serv_list);
	      free_dlink_node(m);
              unset_chcap_usage_counts(source_p);
	    }
	}

      if (IsServer(source_p))
        {
          Count.myserver--;
	  if(ServerInfo.hub)
	    remove_lazylink_flags(source_p->localClient->serverMask);
	  else
	    uplink = NULL;
        }

      if (IsPerson(source_p))
        sendto_realops_flags(FLAGS_CCONN, L_ALL,
                             "Client exiting: %s (%s@%s) [%s] [%s]",
                             source_p->name, source_p->username, source_p->host,
                             comment, source_p->localClient->sockhost);

      log_user_exit(source_p);

      if (source_p->localClient->fd >= 0)
	{
	  if (client_p != NULL && source_p != client_p)
	    sendto_one(source_p, "ERROR :Closing Link: %s %s (%s)",
		       source_p->host, source_p->name, comment);
	  else
	    sendto_one(source_p, "ERROR :Closing Link: %s (%s)",
		       source_p->host, comment);
	}
      /*
      ** Currently only server connections can have
      ** depending remote clients here, but it does no
      ** harm to check for all local clients. In
      ** future some other clients than servers might
      ** have remotes too...
      **
      ** Close the Client connection first and mark it
      ** so that no messages are attempted to send to it.
      ** (The following *must* make MyConnect(source_p) == FALSE!).
      ** It also makes source_p->from == NULL, thus it's unnecessary
      ** to test whether "source_p != target_p" in the following loops.
      */
     close_connection(source_p);
    }

  if(IsServer(source_p))
    {        
      if(ConfigServerHide.hide_servers)
	{
          /* set netsplit message to "me.name *.split" to still show 
	   * that its a split, but hide the servers splitting
	   */
	  ircsprintf(comment1,"%s *.split", me.name);
	}
      else
	{
	  if((source_p->serv) && (source_p->serv->up))
	    strcpy(comment1, source_p->serv->up);
	  else
	    strcpy(comment1, "<Unknown>");

	  strcat(comment1," ");
	  strcat(comment1, source_p->name);
	}

      remove_dependents(client_p, source_p, from, comment, comment1);

      if (source_p->servptr == &me)
        {
          sendto_realops_flags(FLAGS_ALL, L_ALL,
		       "%s was connected for %d seconds.  %d/%d sendK/recvK.",
			       source_p->name, (int)(CurrentTime - source_p->firsttime),
			       source_p->localClient->sendK,
			       source_p->localClient->receiveK);
          ilog(L_NOTICE, "%s was connected for %d seconds.  %d/%d sendK/recvK.",
              source_p->name, CurrentTime - source_p->firsttime, 
              source_p->localClient->sendK, source_p->localClient->receiveK);
        }
    }
  exit_one_client(client_p, source_p, from, comment);
  return client_p == source_p ? CLIENT_EXITED : 0;
}
Пример #18
0
/** Check for clients that have not sent a ping response recently.
 * Reschedules itself to run again at the appropriate time.
 * @param[in] ev Timer event (ignored).
 */
static void check_pings(struct Event* ev) {
  int expire     = 0;
  int next_check = CurrentTime;
  int max_ping   = 0;
  int i;

  assert(ET_EXPIRE == ev_type(ev));
  assert(0 != ev_timer(ev));

  next_check += feature_int(FEAT_PINGFREQUENCY);
  
  /* Scan through the client table */
  for (i=0; i <= HighestFd; i++) {
    struct Client *cptr = LocalClientArray[i];
   
    if (!cptr)
      continue;
     
    assert(&me != cptr);  /* I should never be in the local client array! */
   

    /* Remove dead clients. */
    if (IsDead(cptr)) {
      exit_client(cptr, cptr, &me, cli_info(cptr));
      continue;
    }

    max_ping = IsRegistered(cptr) ? client_get_ping(cptr) :
      feature_int(FEAT_CONNECTTIMEOUT);
   
    Debug((DEBUG_DEBUG, "check_pings(%s)=status:%s limit: %d current: %d",
	   cli_name(cptr),
	   IsPingSent(cptr) ? "[Ping Sent]" : "[]", 
	   max_ping, (int)(CurrentTime - cli_lasttime(cptr))));

    /* If it's a server and we have not sent an AsLL lately, do so. */
    if (IsServer(cptr)) {
      if (CurrentTime - cli_serv(cptr)->asll_last >= max_ping) {
        char *asll_ts;

        SetPingSent(cptr);
        cli_serv(cptr)->asll_last = CurrentTime;
        expire = cli_serv(cptr)->asll_last + max_ping;
        asll_ts = militime_float(NULL);
        sendcmdto_prio_one(&me, CMD_PING, cptr, "!%s %s %s", asll_ts,
                           cli_name(cptr), asll_ts);
      }

      expire = cli_serv(cptr)->asll_last + max_ping;
      if (expire < next_check)
        next_check = expire;
    }

    /* Ok, the thing that will happen most frequently, is that someone will
     * have sent something recently.  Cover this first for speed.
     * -- 
     * If it's an unregistered client and hasn't managed to register within
     * max_ping then it's obviously having problems (broken client) or it's
     * just up to no good, so we won't skip it, even if its been sending
     * data to us. 
     * -- hikari
     */
    if ((CurrentTime-cli_lasttime(cptr) < max_ping) && IsRegistered(cptr)) {
      expire = cli_lasttime(cptr) + max_ping;
      if (expire < next_check) 
	next_check = expire;
      continue;
    }

    /* Unregistered clients pingout after max_ping seconds, they don't
     * get given a second chance - if they were then people could not quite
     * finish registration and hold resources without being subject to k/g
     * lines
     */
    if (!IsRegistered(cptr)) {
      assert(!IsServer(cptr));
      /* If client authorization time has expired, ask auth whether they
       * should be checked again later. */
      if ((CurrentTime-cli_firsttime(cptr) >= max_ping)
          && auth_ping_timeout(cptr))
        continue;
      /* OK, they still have enough time left, so we'll just skip to the
       * next client.  Set the next check to be when their time is up, if
       * that's before the currently scheduled next check -- hikari */
      expire = cli_firsttime(cptr) + max_ping;
      if (expire < next_check)
        next_check = expire;
      continue;
    }

    /* Quit the client after max_ping*2 - they should have answered by now */
    if (CurrentTime-cli_lasttime(cptr) >= (max_ping*2) )
    {
      /* If it was a server, then tell ops about it. */
      if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
        sendto_opmask_butone(0, SNO_OLDSNO,
                             "No response from %s, closing link",
                             cli_name(cptr));
      exit_client_msg(cptr, cptr, &me, "Ping timeout");
      continue;
    }
    
    if (!IsPingSent(cptr))
    {
      /* If we haven't PINGed the connection and we haven't heard from it in a
       * while, PING it to make sure it is still alive.
       */
      SetPingSent(cptr);

      /* If we're late in noticing don't hold it against them :) */
      cli_lasttime(cptr) = CurrentTime - max_ping;
      
      if (IsUser(cptr))
        sendrawto_one(cptr, MSG_PING " :%s", cli_name(&me));
      else
        sendcmdto_prio_one(&me, CMD_PING, cptr, ":%s", cli_name(&me));
    }
    
    expire = cli_lasttime(cptr) + max_ping * 2;
    if (expire < next_check)
      next_check=expire;
  }
  
  assert(next_check >= CurrentTime);
  
  Debug((DEBUG_DEBUG, "[%i] check_pings() again in %is",
	 CurrentTime, next_check-CurrentTime));
  
  timer_add(&ping_timer, check_pings, 0, TT_ABSOLUTE, next_check);
}
Пример #19
0
/*
 * parse a buffer.
 *
 * NOTE: parse() should not be called recusively by any other functions!
 */
int parse(aClient *cptr, char *pbuffer, char *bufend)
{
  aClient *from = cptr;
  char  *ch;
  char  *s;
  size_t i;
  char* numeric = 0;
  unsigned int paramcount;
  struct Message *mptr;

  Debug((DEBUG_DEBUG, "Parsing %s: %s",
         get_client_name(cptr, TRUE), pbuffer));

  if (IsDead(cptr))
    return -1;

  s = sender;
  *s = '\0';

  for (ch = pbuffer; *ch == ' '; ch++)   /* skip spaces */
    /* null statement */ ;

  para[0] = from->name;
  if (*ch == ':')
    {
      ch++;

      /*
      ** Copy the prefix to 'sender' assuming it terminates
      ** with SPACE (or NULL, which is an error, though).
      */
      for (i = 0; *ch && *ch != ' '; i++ )
	{
	  if (i < (sizeof(sender)-1))
	    *s++ = *ch; /* leave room for NULL */
	  ch++;
	}
      *s = '\0';
      i = 0;

      /*
      ** Actually, only messages coming from servers can have
      ** the prefix--prefix silently ignored, if coming from
      ** a user client...
      **
      ** ...sigh, the current release "v2.2PL1" generates also
      ** null prefixes, at least to NOTIFY messages (e.g. it
      ** puts "sptr->nickname" as prefix from server structures
      ** where it's null--the following will handle this case
      ** as "no prefix" at all --msa  (": NOTICE nick ...")
      */
      if (*sender && IsServer(cptr))
        {
          from = find_client(sender, (aClient *) NULL);
          if (!from || !match(from->name, sender))
            from = find_server(sender);

          para[0] = sender;
          
          /* Hmm! If the client corresponding to the
           * prefix is not found--what is the correct
           * action??? Now, I will ignore the message
           * (old IRC just let it through as if the
           * prefix just wasn't there...) --msa
           */
          if (!from)
            {
	      sendto_ops_flag_butone(cptr, UMODE_DEBUG, "Unknown prefix (%s) from (%s)",
				     sender, cptr->name);
              Debug((DEBUG_ERROR, "Unknown prefix (%s)(%s) from (%s)",
                     sender, pbuffer, cptr->name));
              ServerStats->is_unpf++;

              remove_unknown(cptr, sender, pbuffer);

              return -1;
            }
          if (from->from != cptr)
            {
              ServerStats->is_wrdi++;
              Debug((DEBUG_ERROR, "Message (%s) coming from (%s)",
                     buffer, cptr->name));
	      sendto_ops_flag_butone(cptr, UMODE_DEBUG, "Message coming from (%s), but %s is at %s",
				     cptr->name, from->name, from->from->name);

              return cancel_clients(cptr, from, pbuffer);
            }
        }
      while (*ch == ' ')
        ch++;
    }

  if (*ch == '\0')
    {
      ServerStats->is_empt++;
      Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
             cptr->name, from->name));
      if (IsServer(cptr))
	sendto_ops_flag_butone(cptr, UMODE_DEBUG, "Empty message from %s:%s",
			       cptr->name, from->name);
      return(-1);
    }

  /*
  ** Extract the command code from the packet.  Point s to the end
  ** of the command code and calculate the length using pointer
  ** arithmetic.  Note: only need length for numerics and *all*
  ** numerics must have parameters and thus a space after the command
  ** code. -avalon
  *
  * ummm???? - Dianora
  */

  if( *(ch + 3) == ' ' && /* ok, lets see if its a possible numeric.. */
      IsDigit(*ch) && IsDigit(*(ch + 1)) && IsDigit(*(ch + 2)) )
    {
      mptr = (struct Message *)NULL;
      numeric = ch;
      paramcount = MAXPARA;
      ServerStats->is_num++;
      s = ch + 3;       /* I know this is ' ' from above if */
      *s++ = '\0';      /* blow away the ' ', and point s to next part */
    }
  else
    {
      s = strchr(ch, ' ');      /* moved from above,now need it here */
      if (s)
        *s++ = '\0';

      mptr = tree_parse(ch);

      if (!mptr || !mptr->cmd)
        {
          /*
           * 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
	   *
	   * Same thing goes for remote commands. If their local
	   * server accepted it, then we should not be sending
	   * a warning message.
	   */
          if (pbuffer[0] != '\0')
            {
              if (IsPerson(from) && MyClient(from))
                sendto_one(from,
                           ":%s %d %s %s :Unknown command",
                           me.name, ERR_UNKNOWNCOMMAND,
                           from->name, ch);
              Debug((DEBUG_ERROR,"Unknown (%s) from %s",
                     ch, get_client_name(cptr, TRUE)));
            }
          ServerStats->is_unco++;
          return(-1);
        }

      paramcount = mptr->parameters;
      i = bufend - ((s) ? s : ch);
      mptr->bytes += i;

      /* 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
       * Opers can send 1 msg per second, burst of ~20
       * -Taner
       */
      if ((mptr->flags & 1) && !(IsServer(cptr)))
        {
#ifdef NO_OPER_FLOOD
#ifndef TRUE_NO_OPER_FLOOD
	  /* note: both have to be defined for the real no-flood */
          if (NoFloodProtection(cptr))
            /* "randomly" (weighted) increase the since */
            cptr->since += (cptr->receiveM % 5) ? 1 : 0;
          else
#else
          if (!NoFloodProtection(cptr))
#endif
#endif
            cptr->since += (2 + i / 120);
        }
    }
  /*
  ** 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' !! */

  /* ZZZ hmmmmmmmm whats this then? */
#if 0
  if (me.user)
    para[0] = sender;
#endif

  i = 1;

  if (s)
    {
      if (paramcount > MAXPARA)
        paramcount = MAXPARA;

      for (;;)
        {
	  while(*s == ' ')	/* tabs are not considered space */
	    *s++ = '\0';

          if(!*s)
            break;

          if (*s == ':')
            {
              /*
              ** The rest is single parameter--can
              ** include blanks also.
              */
              para[i++] = s + 1;
              break;
            }
	  else
	    {
	      para[i++] = s;
              if (i > paramcount)
                {
                  break;
                }
              /* scan for end of string, either ' ' or '\0' */
              while (IsNonEOS(*s))
                s++;
	    }
        }
    }

  para[i] = NULL;
  if (mptr == (struct Message *)NULL)
    return (do_numeric(numeric, cptr, from, i, para));

  mptr->count++;

  /* patch to avoid server flooding from unregistered connects */
  /* check allow_unregistered_use flag I've set up instead of function
     comparing *yech* - Dianora */

  if (!IsRegistered(cptr) && !mptr->allow_unregistered_use )
    {
      /* if its from a possible server connection
       * ignore it.. more than likely its a header thats sneaked through
       */

      if(IsHandshake(cptr) || IsConnecting(cptr) || IsServer(cptr))
        return -1;

      sendto_one(from,
                 ":%s %d %s %s :Register first.",
                 me.name, ERR_NOTREGISTERED,
                 BadPtr(from->name) ? "*" : from->name, ch);
      return -1;
    }

  /* Silently drop the messages that can't be used in the honeypot */
  if (IsHoneypot(cptr) && !mptr->allow_honeypot)
    return -1;

  /* Again, instead of function address comparing, see if
   * this function resets idle time as given from mptr
   * if IDLE_FROM_MSG is undefined, the sense of the flag is reversed.
   * i.e. if the flag is 0, then reset idle time, otherwise don't reset it.
   *
   * - Dianora
   */

  if (IsRegisteredUser(from) && mptr->reset_idle)
    {
      /* If a local registered user, propagate anti-idle updates as needed */
      if (MyClient(from) && from->user && ((from->user->last_sent + MAX_IDLE_DESYNC) < CurrentTime))
	{
#if 0
	  /* Note the misnamed message, this indicates that they are NOT idle */
#ifdef HAVE_LONG_LONG
	  sendto_serv_butone(NULL, ":%s IDLE %s %.1lld", cptr->name, from->name, (long long)CurrentTime);
#else
	  sendto_serv_butone(NULL, ":%s IDLE %s %.1ld", cptr->name, from->name, (long)CurrentTime);
#endif
#endif
	  from->user->last_sent = CurrentTime;
	}
      from->user->last = CurrentTime;
    }

  /* don't allow other commands while a list is blocked. since we treat
     them specially with respect to sendq. */
  if ((IsDoingList(cptr)) && (*mptr->func != m_list))
      return -1;
  return (*mptr->func)(cptr, from, i, para);
}
Пример #20
0
/** Check whether the introduction of a new server would cause a loop
 * or be disallowed by leaf and hub configuration directives.
 * @param[in] cptr Neighbor who sent the message.
 * @param[in] sptr Client that originated the message.
 * @param[out] ghost If non-NULL, receives ghost timestamp for new server.
 * @param[in] host Name of new server.
 * @param[in] numnick Numnick mask of new server.
 * @param[in] timestamp Claimed link timestamp of new server.
 * @param[in] hop Number of hops to the new server.
 * @param[in] junction Non-zero if the new server is still bursting.
 * @return CPTR_KILLED if \a cptr was SQUIT.  0 if some other server
 * was SQUIT.  1 if the new server is allowed.
 */
static int
check_loop_and_lh(struct Client* cptr, struct Client *sptr, time_t *ghost, const char *host, const char *numnick, time_t timestamp, unsigned int hop, int junction)
{
  struct Client* acptr;
  struct Client* LHcptr = NULL;
  struct ConfItem* lhconf;
  enum lh_type active_lh_line = ALLOWED;
  int ii;

  if (ghost)
    *ghost = 0;

  /*
   * Calculate type of connect limit and applicable config item.
   */
  lhconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER);
  assert(lhconf != NULL);
  if (ghost)
  {
    if (!feature_bool(FEAT_HUB))
      for (ii = 0; ii <= HighestFd; ii++)
        if (LocalClientArray[ii] && IsServer(LocalClientArray[ii])) {
          active_lh_line = I_AM_NOT_HUB;
          break;
        }
  }
  else if (hop > lhconf->maximum)
  {
    /* Because "maximum" should be 0 for non-hub links, check whether
     * there is a hub mask -- if not, complain that the server isn't
     * allowed to hub.
     */
    active_lh_line = lhconf->hub_limit ? MAX_HOPS_EXCEEDED : NOT_ALLOWED_TO_HUB;
  }
  else if (lhconf->hub_limit && match(lhconf->hub_limit, host))
  {
    struct Client *ac3ptr;
    active_lh_line = NOT_ALLOWED_TO_HUB;
    if (junction)
      for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
        if (IsJunction(ac3ptr)) {
          LHcptr = ac3ptr;
          break;
        }
  }

  /*
   *  We want to find IsConnecting() and IsHandshake() too,
   *  use FindClient().
   *  The second finds collisions with numeric representation of existing
   *  servers - these shouldn't happen anymore when all upgraded to 2.10.
   *  -- Run
   */
  while ((acptr = FindClient(host))
         || (numnick && (acptr = FindNServer(numnick))))
  {
    /*
     *  This link is trying feed me a server that I already have
     *  access through another path
     *
     *  Do not allow Uworld to do this.
     *  Do not allow servers that are juped.
     *  Do not allow servers that have older link timestamps
     *    then this try.
     *  Do not allow servers that use the same numeric as an existing
     *    server, but have a different name.
     *
     *  If my ircd.conf sucks, I can try to connect to myself:
     */
    if (acptr == &me)
      return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host);
    /*
     * Detect wrong numeric.
     */
    if (0 != ircd_strcmp(cli_name(acptr), host))
    {
      sendcmdto_serv(&me, CMD_WALLOPS, cptr,
                     ":SERVER Numeric Collision: %s != %s",
                     cli_name(acptr), host);
      return exit_client_msg(cptr, cptr, &me,
          "NUMERIC collision between %s and %s."
          " Is your server numeric correct ?", host, cli_name(acptr));
    }
    /*
     *  Kill our try, if we had one.
     */
    if (IsConnecting(acptr))
    {
      if (active_lh_line == ALLOWED && exit_client(cptr, acptr, &me,
          "Just connected via another link") == CPTR_KILLED)
        return CPTR_KILLED;
      /*
       * We can have only ONE 'IsConnecting', 'IsHandshake' or
       * 'IsServer', because new 'IsConnecting's are refused to
       * the same server if we already had it.
       */
      break;
    }
    /*
     * Avoid other nick collisions...
     * This is a doubtful test though, what else would it be
     * when it has a server.name ?
     */
    else if (!IsServer(acptr) && !IsHandshake(acptr))
      return exit_client_msg(cptr, cptr, &me,
                             "Nickname %s already exists!", host);
    /*
     * Our new server might be a juped server:
     */
    else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4)))
    {
      if (!IsServer(sptr))
        return exit_client(cptr, sptr, &me, cli_info(acptr));
      sendcmdto_serv(&me, CMD_WALLOPS, cptr,
                     ":Received :%s SERVER %s from %s !?!",
                     NumServ(cptr), host, cli_name(cptr));
      return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr));
    }
    /*
     * Of course we find the handshake this link was before :)
     */
    else if (IsHandshake(acptr) && acptr == cptr)
      break;
    /*
     * Here we have a server nick collision...
     * We don't want to kill the link that was last /connected,
     * but we neither want to kill a good (old) link.
     * Therefor we kill the second youngest link.
     */
    if (1)
    {
      struct Client* c2ptr = 0;
      struct Client* c3ptr = acptr;
      struct Client* ac2ptr;
      struct Client* ac3ptr;

      /* Search youngest link: */
      for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
        if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
          c3ptr = ac3ptr;
      if (IsServer(sptr))
      {
        for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
          if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
            c3ptr = ac3ptr;
      }
      if (timestamp > cli_serv(c3ptr)->timestamp)
      {
        c3ptr = 0;
        c2ptr = acptr;          /* Make sure they differ */
      }
      /* Search second youngest link: */
      for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
        if (ac2ptr != c3ptr &&
            cli_serv(ac2ptr)->timestamp >
            (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
          c2ptr = ac2ptr;
      if (IsServer(sptr))
      {
        for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
          if (ac2ptr != c3ptr &&
              cli_serv(ac2ptr)->timestamp >
              (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
            c2ptr = ac2ptr;
      }
      if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
        c2ptr = 0;
      /* If timestamps are equal, decide which link to break
       *  by name.
       */
      if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) ==
          (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp))
      {
        const char *n2, *n2up, *n3, *n3up;
        if (c2ptr)
        {
          n2 = cli_name(c2ptr);
          n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up);
        }
        else
        {
          n2 = host;
          n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
        }
        if (c3ptr)
        {
          n3 = cli_name(c3ptr);
          n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up);
        }
        else
        {
          n3 = host;
          n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
        }
        if (strcmp(n2, n2up) > 0)
          n2 = n2up;
        if (strcmp(n3, n3up) > 0)
          n3 = n3up;
        if (strcmp(n3, n2) > 0)
        {
          ac2ptr = c2ptr;
          c2ptr = c3ptr;
          c3ptr = ac2ptr;
        }
      }
      /* Now squit the second youngest link: */
      if (!c2ptr)
        return exit_new_server(cptr, sptr, host, timestamp,
                               "server %s already exists and is %ld seconds younger.",
                               host, (long)cli_serv(acptr)->timestamp - (long)timestamp);
      else if (cli_from(c2ptr) == cptr || IsServer(sptr))
      {
        struct Client *killedptrfrom = cli_from(c2ptr);
        if (active_lh_line != ALLOWED)
        {
          /*
           * If the L: or H: line also gets rid of this link,
           * we sent just one squit.
           */
          if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
            break;
          /*
           * If breaking the loop here solves the L: or H:
           * line problem, we don't squit that.
           */
          if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
            active_lh_line = ALLOWED;
          else
          {
            /*
             * If we still have a L: or H: line problem,
             * we prefer to squit the new server, solving
             * loop and L:/H: line problem with only one squit.
             */
            LHcptr = 0;
            break;
          }
        }
        /*
         * If the new server was introduced by a server that caused a
         * Ghost less then 20 seconds ago, this is probably also
         * a Ghost... (20 seconds is more then enough because all
         * SERVER messages are at the beginning of a net.burst). --Run
         */
        if (CurrentTime - cli_serv(cptr)->ghost < 20)
        {
          killedptrfrom = cli_from(acptr);
          if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
            return CPTR_KILLED;
        }
        else if (exit_client_msg(cptr, c2ptr, &me,
            "Loop <-- %s (new link is %ld seconds younger)", host,
            (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) -
            (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED)
          return CPTR_KILLED;
        /*
         * Did we kill the incoming server off already ?
         */
        if (killedptrfrom == cptr)
          return 0;
      }
      else
      {
        if (active_lh_line != ALLOWED)
        {
          if (LHcptr && a_kills_b_too(LHcptr, acptr))
            break;
          if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
            active_lh_line = ALLOWED;
          else
          {
            LHcptr = 0;
            break;
          }
        }
        /*
         * We can't believe it is a lagged server message
         * when it directly connects to us...
         * kill the older link at the ghost, rather then
         * at the second youngest link, assuming it isn't
         * a REAL loop.
         */
        if (ghost)
          *ghost = CurrentTime;            /* Mark that it caused a ghost */
        if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
          return CPTR_KILLED;
        break;
      }
    }
  }

  if (active_lh_line != ALLOWED)
  {
    if (!LHcptr)
      LHcptr = sptr;
    if (active_lh_line == MAX_HOPS_EXCEEDED)
    {
      return exit_client_msg(cptr, LHcptr, &me,
                             "Maximum hops exceeded for %s at %s",
                             cli_name(cptr), host);
    }
    else if (active_lh_line == NOT_ALLOWED_TO_HUB)
    {
      return exit_client_msg(cptr, LHcptr, &me,
                             "%s is not allowed to hub for %s",
                             cli_name(cptr), host);
    }
    else /* I_AM_NOT_HUB */
    {
      ServerStats->is_ref++;
      return exit_client(cptr, LHcptr, &me, "I'm a leaf, define the HUB feature");
    }
  }

  return 1;
}
Пример #21
0
Файл: send.c Проект: ahf/irc
/*
** send_queued
**	This function is called from the main select-loop (or whatever)
**	when there is a chance the some output would be possible. This
**	attempts to empty the send queue as far as possible...
*/
int	send_queued(aClient *to)
{
	char	*msg;
	int	len, rlen, more = 0;
	aClient *bysptr = NULL;

	/*
	** Once socket is marked dead, we cannot start writing to it,
	** even if the error is removed...
	*/
	if (IsDead(to))
	    {
		/*
		** Actually, we should *NEVER* get here--something is
		** not working correct if send_queued is called for a
		** dead socket... --msa
		*/
		return -1;
	    }
#ifdef	ZIP_LINKS
	/*
	** Here, we must make sure than nothing will be left in to->zip->outbuf
	** This buffer needs to be compressed and sent if all the sendQ is sent
	*/
	if ((to->flags & FLAGS_ZIP) && to->zip->outcount)
	    {
		if (DBufLength(&to->sendQ) > 0)
			more = 1;
		else
		    {
			msg = zip_buffer(to, NULL, &len, 1);
			
			if (len == -1)
			       return dead_link(to,
						"fatal error in zip_buffer()");

			if (dbuf_put(&to->sendQ, msg, len) < 0)
			    {
				to->exitc = EXITC_MBUF;
				return dead_link(to,
					 "Buffer allocation error for %s",
					get_client_name(to, FALSE));
			    }
		    }
	    }
#endif
	while (DBufLength(&to->sendQ) > 0 || more)
	    {
		msg = dbuf_map(&to->sendQ, &len);
					/* Returns always len > 0 */
		if ((rlen = deliver_it(to, msg, len)) < 0)
		{
			if ( (IsConnecting(to) || IsHandshake(to))
			     && to->serv && to->serv->byuid[0])
			{
				bysptr = find_uid(to->serv->byuid, NULL);
				if (bysptr && !MyConnect(bysptr))
				{
					sendto_one(bysptr, ":%s NOTICE %s :"
					"Write error (%s) to %s, closing link",
					ME, bysptr->name, strerror(-rlen),
					to->name);
				}
			}
			return dead_link(to,
				"Write error (%s) to %s, closing link",
				strerror(-rlen), get_client_name(to, FALSE));
		}
		(void)dbuf_delete(&to->sendQ, rlen);
		to->lastsq = DBufLength(&to->sendQ)/1024;
		if (rlen < len) /* ..or should I continue until rlen==0? */
			break;

#ifdef	ZIP_LINKS
		if (DBufLength(&to->sendQ) == 0 && more)
		    {
			/*
			** The sendQ is now empty, compress what's left
			** uncompressed and try to send it too
			*/
			more = 0;
			msg = zip_buffer(to, NULL, &len, 1);

			if (len == -1)
			       return dead_link(to,
						"fatal error in zip_buffer()");

			if (dbuf_put(&to->sendQ, msg, len) < 0)
			    {
				to->exitc = EXITC_MBUF;
				return dead_link(to,
					 "Buffer allocation error for %s",
					get_client_name(to, FALSE));
			    }
		    }
#endif
	    }

	return (IsDead(to)) ? -1 : 0;
}
Пример #22
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));
}
Пример #23
0
/*
** deliver_it
**	Attempt to send a sequence of bytes to the connection.
**	Returns
**
**	< 0	Some fatal error occurred, (but not EWOULDBLOCK).
**		This return is a request to close the socket and
**		clean up the link.
**	
**	>= 0	No real error occurred, returns the number of
**		bytes actually transferred. EWOULDBLOCK and other
**		possibly similar conditions should be mapped to
**		zero return. Upper level routine will have to
**		decide what to do with those unwritten bytes...
**
**	*NOTE*	alarm calls have been preserved, so this should
**		work equally well whether blocking or non-blocking
**		mode is used...
**
**	*NOTE*	I nuked 'em.  At the load of current ircd servers
**		you can't run with stuff that blocks. And we don't.
*/
int  deliver_it(aClient *cptr, char *str, int len)
{
	int  retval;
	aClient *acpt = cptr->listener;

#ifdef	DEBUGMODE
	writecalls++;
#endif
#ifdef VMS
	retval = netwrite(cptr->fd, str, len);
#else
	if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr)
	    && !IsHandshake(cptr) 
#ifdef USE_SSL
	    && !IsSSLHandshake(cptr)
#endif 
 
	    && !IsUnknown(cptr)))
	{
		str[len] = '\0';
		sendto_ops
		    ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s",
		    cptr->name, cptr->status, IsDead(cptr) ? "DEAD" : "", str);
		return -1;
	}

#ifdef USE_SSL
	if (cptr->flags & FLAGS_SSL)
		 retval = ircd_SSL_write(cptr, str, len);	
	else
#endif
		retval = send(cptr->fd, str, len, 0);
	/*
	   ** Convert WOULDBLOCK to a return of "0 bytes moved". This
	   ** should occur only if socket was non-blocking. Note, that
	   ** all is Ok, if the 'write' just returns '0' instead of an
	   ** error and errno=EWOULDBLOCK.
	   **
	   ** ...now, would this work on VMS too? --msa
	 */
# ifndef _WIN32
	if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN ||
	    errno == ENOBUFS))
# else
		if (retval < 0 && (WSAGetLastError() == WSAEWOULDBLOCK ||
		    WSAGetLastError() == WSAENOBUFS))
# endif
		{
			retval = 0;
			SetBlocked(cptr);
		}
		else if (retval > 0)
		{
			ClearBlocked(cptr);
		}

#endif
#ifdef DEBUGMODE
	if (retval < 0)
	{
		writeb[0]++;
		Debug((DEBUG_ERROR, "write error (%s) to %s", STRERROR(ERRNO), cptr->name));

	}
	else if (retval == 0)
		writeb[1]++;
	else if (retval < 16)
		writeb[2]++;
	else if (retval < 32)
		writeb[3]++;
	else if (retval < 64)
		writeb[4]++;
	else if (retval < 128)
		writeb[5]++;
	else if (retval < 256)
		writeb[6]++;
	else if (retval < 512)
		writeb[7]++;
	else if (retval < 1024)
		writeb[8]++;
	else
		writeb[9]++;
#endif
	if (retval > 0)
	{
		cptr->sendB += retval;
		me.sendB += retval;
		if (cptr->sendB > 1023)
		{
			cptr->sendK += (cptr->sendB >> 10);
			cptr->sendB &= 0x03ff;	/* 2^10 = 1024, 3ff = 1023 */
		}
Пример #24
0
/* Rewritten by Run - 24 sept 94 */
static void exit_one_client(struct Client* bcptr, const char* comment)
{
  struct SLink *lp;
  struct Ban *bp;

  if (cli_serv(bcptr) && cli_serv(bcptr)->client_list)  /* Was SetServerYXX called ? */
    ClearServerYXX(bcptr);      /* Removes server from server_list[] */
  if (IsUser(bcptr)) {
    /*
     * clear out uping requests
     */
    if (IsUPing(bcptr))
      uping_cancel(bcptr, 0);
    /*
     * Stop a running /LIST clean
     */
    if (MyUser(bcptr) && cli_listing(bcptr)) {
      MyFree(cli_listing(bcptr));
      cli_listing(bcptr) = NULL;
    }
    /*
     * If a person is on a channel, send a QUIT notice
     * to every client (person) on the same channel (so
     * that the client can show the "**signoff" message).
     * (Note: The notice is to the local clients *only*)
     */
    sendcmdto_common_channels_butone(bcptr, CMD_QUIT, NULL, ":%s", comment);

    remove_user_from_all_channels(bcptr);

    /* Clean up invitefield */
    while ((lp = cli_user(bcptr)->invited))
      del_invite(bcptr, lp->value.chptr);

    /* Clean up silencefield */
    while ((bp = cli_user(bcptr)->silence)) {
      cli_user(bcptr)->silence = bp->next;
      free_ban(bp);
    }

    /* Clean up snotice lists */
    if (MyUser(bcptr))
      set_snomask(bcptr, ~0, SNO_DEL);

    if (IsInvisible(bcptr)) {
      assert(UserStats.inv_clients > 0);
      --UserStats.inv_clients;
    }
    if (IsOper(bcptr)) {
      assert(UserStats.opers > 0);
      --UserStats.opers;
    }
    if (MyConnect(bcptr))
      Count_clientdisconnects(bcptr, UserStats);
    else
      Count_remoteclientquits(UserStats, bcptr);
  }
  else if (IsServer(bcptr))
  {
    /* Remove downlink list node of uplink */
    remove_dlink(&(cli_serv(cli_serv(bcptr)->up))->down, cli_serv(bcptr)->updown);
    cli_serv(bcptr)->updown = 0;

    if (MyConnect(bcptr))
      Count_serverdisconnects(UserStats);
    else
      Count_remoteserverquits(UserStats);
  }
  else if (IsMe(bcptr))
  {
    sendto_opmask_butone(0, SNO_OLDSNO, "ERROR: tried to exit me! : %s",
			 comment);
    return;                     /* ...must *never* exit self! */
  }
  else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr))
    Count_unknowndisconnects(UserStats);

  /*
   * Update IPregistry
   */
  if (IsIPChecked(bcptr))
    IPcheck_disconnect(bcptr);

  /* 
   * Remove from serv->client_list
   * NOTE: user is *always* NULL if this is a server
   */
  if (cli_user(bcptr)) {
    assert(!IsServer(bcptr));
    /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
    RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr));
  }

  /* Remove bcptr from the client list */
#ifdef DEBUGMODE
  if (hRemClient(bcptr) != 0)
    Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p",
          bcptr, cli_name(bcptr), cli_from(bcptr) ? cli_sockhost(cli_from(bcptr)) : "??host",
          cli_from(bcptr), cli_next(bcptr), cli_prev(bcptr), cli_fd(bcptr),
          cli_status(bcptr), cli_user(bcptr)));
#else
  hRemClient(bcptr);
#endif
  remove_client_from_list(bcptr);
}
Пример #25
0
/*
** deliver_it
**	Attempt to send a sequence of bytes to the connection.
**	Returns
**
**	< 0	Some fatal error occurred, (but not EWOULDBLOCK).
**		This return is a request to close the socket and
**		clean up the link.
**
**	>= 0	No real error occurred, returns the number of
**		bytes actually transferred. EWOULDBLOCK and other
**		possibly similar conditions should be mapped to
**		zero return. Upper level routine will have to
**		decide what to do with those unwritten bytes...
**
**	*NOTE*	alarm calls have been preserved, so this should
**		work equally well whether blocking or non-blocking
**		mode is used...
**
**	*NOTE*	I nuked 'em.  At the load of current ircd servers
**		you can't run with stuff that blocks. And we don't.
*/
int  deliver_it(aClient *cptr, char *str, int len)
{
    int  retval;

    if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr)
                         && !IsHandshake(cptr)
#ifdef USE_SSL
                         && !IsSSLHandshake(cptr)
#endif

                         && !IsUnknown(cptr)))
    {
        str[len] = '\0';
        sendto_ops
        ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s",
         cptr->name, cptr->status, IsDead(cptr) ? "DEAD" : "", str);
        return -1;
    }

#ifdef USE_SSL
    if (IsSSL(cptr) && cptr->ssl != NULL)
    {
        retval = SSL_write(cptr->ssl, str, len);

        if (retval < 0)
        {
            switch (SSL_get_error(cptr->ssl, retval))
            {
            case SSL_ERROR_WANT_READ:
                /* retry later */
                return 0;
            case SSL_ERROR_WANT_WRITE:
                SET_ERRNO(P_EWOULDBLOCK);
                break;
            case SSL_ERROR_SYSCALL:
                break;
            case SSL_ERROR_SSL:
                if (ERRNO == P_EAGAIN)
                    break;
            default:
                return 0;
            }
        }
    }
    else
#endif
        retval = send(cptr->fd, str, len, 0);
    /*
       ** Convert WOULDBLOCK to a return of "0 bytes moved". This
       ** should occur only if socket was non-blocking. Note, that
       ** all is Ok, if the 'write' just returns '0' instead of an
       ** error and errno=EWOULDBLOCK.
       **
       ** ...now, would this work on VMS too? --msa
     */
    if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN ||
                       errno == ENOBUFS))
        retval = 0;

    if (retval > 0)
    {
        cptr->sendB += retval;
        me.sendB += retval;
        if (cptr->sendB > 1023)
        {
            cptr->sendK += (cptr->sendB >> 10);
            cptr->sendB &= 0x03ff;	/* 2^10 = 1024, 3ff = 1023 */
        }
Пример #26
0
/**
 * Exits a client of *any* type (user, server, etc)
 * from this server. Also, this generates all necessary prototol
 * messages that this exit may cause.
 *
 * This function implicitly exits all other clients depending on
 * this connection.
 *
 * For convenience, this function returns a suitable value for
 * m_function return value:
 *
 *   CPTR_KILLED     if (cptr == bcptr)
 *   0                if (cptr != bcptr)
 *
 * This function can be called in two ways:
 * 1) From before or in parse(), exiting the 'cptr', in which case it was
 *    invoked as exit_client(cptr, cptr, &me,...), causing it to always
 *    return CPTR_KILLED.
 * 2) Via parse from a m_function call, in which case it was invoked as
 *    exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client
 *    that generated the message in a way that we can assume he already
 *    did remove acptr from memory himself (or in other cases we don't mind
 *    because he will be delinked.) Or invoked as:
 *    exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should
 *    be removed.
 * In general: No generated SQUIT or QUIT should be sent to source link
 * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too).
 *
 * --Run
 * @param cptr Connection currently being handled by read_message.
 * @param victim Client being killed.
 * @param killer Client that made the decision to remove \a victim.
 * @param comment Reason for the exit.
 * @return CPTR_KILLED if cptr == bcptr, else 0.
 */
int exit_client(struct Client *cptr,
    struct Client* victim,
    struct Client* killer,
    const char* comment)
{
  struct Client* acptr = 0;
  struct DLink *dlp;
  time_t on_for;

  char comment1[HOSTLEN + HOSTLEN + 2];
  assert(killer);
  if (MyConnect(victim))
  {
    SetFlag(victim, FLAG_CLOSING);

    if (feature_bool(FEAT_CONNEXIT_NOTICES) && IsUser(victim))
      sendto_opmask_butone(0, SNO_CONNEXIT,
                           "Client exiting: %s (%s@%s) [%s] [%s] <%s%s>",
                           cli_name(victim), cli_user(victim)->username,
                           cli_user(victim)->host, comment,
                           ircd_ntoa(&cli_ip(victim)),
                           NumNick(victim) /* two %s's */);
    update_load();

    on_for = CurrentTime - cli_firsttime(victim);

    if (IsUser(victim) || IsUserPort(victim))
      auth_send_exit(victim);

    if (IsUser(victim))
      log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s",
		cli_firsttime(victim), on_for,
		cli_user(victim)->username, cli_sockhost(victim),
                ircd_ntoa(&cli_ip(victim)),
                IsAccount(victim) ? cli_username(victim) : "0",
                NumNick(victim), /* two %s's */
                cli_name(victim), cli_info(victim));

    if (victim != cli_from(killer)  /* The source knows already */
        && IsClient(victim))    /* Not a Ping struct or Log file */
    {
      if (IsServer(victim) || IsHandshake(victim))
	sendcmdto_one(killer, CMD_SQUIT, victim, "%s 0 :%s", cli_name(&me), comment);
      else if (!IsConnecting(victim)) {
        if (!IsDead(victim)) {
	  if (IsServer(victim))
	    sendcmdto_one(killer, CMD_ERROR, victim,
			  ":Closing Link: %s by %s (%s)", cli_name(victim),
			  cli_name(killer), comment);
	  else
	    sendrawto_one(victim, MSG_ERROR " :Closing Link: %s by %s (%s)",
			  cli_name(victim),
                          cli_name(IsServer(killer) ? &his : killer),
			  comment);
	}
      }
      if ((IsServer(victim) || IsHandshake(victim) || IsConnecting(victim)) &&
          (killer == &me || (IsServer(killer) &&
          (strncmp(comment, "Leaf-only link", 14) ||
          strncmp(comment, "Non-Hub link", 12)))))
      {
        /*
         * Note: check user == user needed to make sure we have the same
         * client
         */
        if (cli_serv(victim)->user && *(cli_serv(victim))->by &&
            (acptr = findNUser(cli_serv(victim)->by))) {
          if (cli_user(acptr) == cli_serv(victim)->user) {
	    sendcmdto_one(&me, CMD_NOTICE, acptr,
			  "%C :Link with %s canceled: %s", acptr,
			  cli_name(victim), comment);
          }
          else {
            /*
             * not right client, set by to empty string
             */
            acptr = 0;
            *(cli_serv(victim))->by = '\0';
          }
        }
        if (killer == &me)
	  sendto_opmask_butone(acptr, SNO_OLDSNO, "Link with %s canceled: %s",
			       cli_name(victim), comment);
      }
    }
    /*
     *  Close the Client connection first.
     */
    close_connection(victim);
  }

  if (IsServer(victim))
  {
    if (feature_bool(FEAT_HIS_NETSPLIT))
      strcpy(comment1, "*.net *.split");
    else
    {
      strcpy(comment1, cli_name(cli_serv(victim)->up));
      strcat(comment1, " ");
      strcat(comment1, cli_name(victim));
    }

    if (IsUser(killer))
      sendto_opmask_butone(killer, SNO_OLDSNO, "%s SQUIT by %s [%s]:",
			   (cli_user(killer)->server == victim ||
			    cli_user(killer)->server == cli_serv(victim)->up) ?
			   "Local" : "Remote",
			   get_client_name(killer, HIDE_IP),
			   cli_name(cli_user(killer)->server));
    else if (killer != &me && cli_serv(victim)->up != killer)
      sendto_opmask_butone(0, SNO_OLDSNO, "Received SQUIT %s from %s :",
			   cli_name(victim), IsServer(killer) ? cli_name(killer) :
			   get_client_name(killer, HIDE_IP));
    sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)",
			 cli_serv(victim)->up, victim, comment);
  }

  /*
   * First generate the needed protocol for the other server links
   * except the source:
   */
  for (dlp = cli_serv(&me)->down; dlp; dlp = dlp->next) {
    if (dlp->value.cptr != cli_from(killer) && dlp->value.cptr != victim)
    {
      if (IsServer(victim))
	sendcmdto_one(killer, CMD_SQUIT, dlp->value.cptr, "%s %Tu :%s",
		      cli_name(victim), cli_serv(victim)->timestamp, comment);
      else if (IsUser(victim) && !HasFlag(victim, FLAG_KILLED))
	sendcmdto_one(victim, CMD_QUIT, dlp->value.cptr, ":%s", comment);
    }
  }
  /* Then remove the client structures */
  if (IsServer(victim))
    exit_downlinks(victim, killer, comment1);
  exit_one_client(victim, comment);

  /*
   *  cptr can only have been killed if it was cptr itself that got killed here,
   *  because cptr can never have been a dependent of victim    --Run
   */
  return (cptr == victim) ? CPTR_KILLED : 0;
}
Пример #27
0
/** Handle a SERVER message from an unregistered connection.
 *
 * \a parv has the following elements:
 * \li \a parv[1] is the server name
 * \li \a parv[2] is the hop count to the server
 * \li \a parv[3] is the start timestamp for the server
 * \li \a parv[4] is the link timestamp
 * \li \a parv[5] is the protocol version (P10 or J10)
 * \li \a parv[6] is the numnick mask for the server
 * \li \a parv[7] is a string of flags like +hs to mark hubs and services
 * \li \a parv[\a parc - 1] is the server description
 *
 * See @ref m_functions for discussion of the arguments.
 * @param[in] cptr Client that sent us the message.
 * @param[in] sptr Original source of message.
 * @param[in] parc Number of arguments.
 * @param[in] parv Argument vector.
 */
int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  char*            host;
  struct ConfItem* aconf;
  struct Jupe*     ajupe;
  unsigned int     hop;
  int              ret;
  unsigned short   prot;
  time_t           start_timestamp;
  time_t           timestamp;
  time_t           recv_time;
  time_t           ghost;

  if (IsUserPort(cptr))
    return exit_client_msg(cptr, cptr, &me,
                           "Cannot connect a server to a user port");

  if (parc < 8)
  {
    need_more_params(sptr, "SERVER");
    return exit_client(cptr, cptr, &me, "Need more parameters");
  }
  host = clean_servername(parv[1]);
  if (!host)
  {
    sendto_opmask(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
                  host, cli_name(cptr));
    return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
  }

  if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe))
    return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe));

  /* check connection rules */
  if (0 != conf_eval_crule(host, CRULE_ALL)) {
    ServerStats->is_ref++;
    sendto_opmask(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr));
    return exit_client(cptr, cptr, &me, "Disallowed by connection rule");
  }

  log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", host,
	    cli_sockhost(cptr), cli_sock_ip(cptr));

  /*
   * Detect protocol
   */
  hop = atoi(parv[2]);
  start_timestamp = atoi(parv[3]);
  timestamp = atoi(parv[4]);
  prot = parse_protocol(parv[5]);
  if (!prot)
    return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
  else if (prot < atoi(MINOR_PROTOCOL))
    return exit_new_server(cptr, sptr, host, timestamp,
                           "Incompatible protocol: %s", parv[5]);

  Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
	 host, parv[4], start_timestamp, cli_serv(&me)->timestamp));

  if (timestamp < OLDEST_TS || start_timestamp < OLDEST_TS)
    return exit_client_msg(cptr, sptr, &me,
        "Bogus timestamps (%s %s)", parv[3], parv[4]);

  /* If the server had a different name before, change it. */
  if (!EmptyString(cli_name(cptr)) &&
      (IsUnknown(cptr) || IsHandshake(cptr)) &&
      0 != ircd_strcmp(cli_name(cptr), host))
    hChangeClient(cptr, host);
  ircd_strncpy(cli_name(cptr), host, HOSTLEN);
  ircd_strncpy(cli_info(cptr), parv[parc-1][0] ? parv[parc-1] : cli_name(&me), REALLEN);
  cli_hopcount(cptr) = hop;

  if (conf_check_server(cptr)) {
    ++ServerStats->is_ref;
    sendto_opmask(0, SNO_OLDSNO, "Received unauthorized connection from %s",
                  cli_name(cptr));
    log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "Received unauthorized "
              "connection from %C [%s]", cptr,
              ircd_ntoa(&cli_ip(cptr)));
    return exit_client(cptr, cptr, &me, "No Connect block");
  }

  host = cli_name(cptr);

  update_load();

  if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) {
    ++ServerStats->is_ref;
    sendto_opmask(0, SNO_OLDSNO, "Access denied. No conf line for server %s",
                  cli_name(cptr));
    return exit_client_msg(cptr, cptr, &me,
                           "Access denied. No conf line for server %s", cli_name(cptr));
  }

#if defined(USE_SSL)
  if (!verify_sslclifp(cptr, aconf)) {
    ++ServerStats->is_ref;
    sendto_opmask(0, SNO_OLDSNO, "Access denied (SSL fingerprint mismatch) %s",
                  cli_name(cptr));
    return exit_client_msg(cptr, cptr, &me,
                           "No Access (SSL fingerprint mismatch) %s", cli_name(cptr));
  }
#endif

  if (*aconf->passwd && !!strcmp(aconf->passwd, cli_passwd(cptr))) {
    ++ServerStats->is_ref;
    sendto_opmask(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s",
                  cli_name(cptr));
    return exit_client_msg(cptr, cptr, &me,
                           "No Access (passwd mismatch) %s", cli_name(cptr));
  }

  memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr)));

  ret = check_loop_and_lh(cptr, sptr, &ghost, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, 1);
  if (ret != 1)
    return ret;

  make_server(cptr);
  cli_serv(cptr)->timestamp = timestamp;
  cli_serv(cptr)->prot = prot;
  cli_serv(cptr)->ghost = ghost;
  memset(cli_privs(cptr), 255, sizeof(struct Privs));
  ClrPriv(cptr, PRIV_SET);
  SetServerYXX(cptr, cptr, parv[6]);
  update_uworld_flags(cptr);

  if (*parv[7] == '+')
    set_server_flags(cptr, parv[7] + 1);

  recv_time = TStime();
  check_start_timestamp(cptr, timestamp, start_timestamp, recv_time);
  ret = server_estab(cptr, aconf);

  if (feature_bool(FEAT_RELIABLE_CLOCK) &&
      abs(cli_serv(cptr)->timestamp - recv_time) > 30) {
    sendto_opmask(0, SNO_OLDSNO, "Connected to a net with a "
                  "timestamp-clock difference of %Td seconds! "
                  "Used SETTIME to correct this.",
                  timestamp - recv_time);
    sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(),
		       cli_name(&me));
  }

  return ret;
}
Пример #28
0
static      time_t
check_pings(time_t currenttime)
{
aClient 	*cptr;
aConfItem 	*aconf = (aConfItem *) NULL;
int     	 killflag, zkillflag, ping = 0, i;
time_t      	 oldest = 0; /* timeout removed, see EXPLANATION below */
char       	*reason, *ktype, fbuf[512];
char 		*errtxt = "No response from %s, closing link";


   for (i = 0; i <= highest_fd; i++) 
   {
      if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr))
	 continue;

       /* Note: No need to notify opers here. It's 
	* already done when "FLAGS_DEADSOCKET" is set.
        */

      if (cptr->flags & FLAGS_DEADSOCKET) 
      {
	 (void) exit_client(cptr, cptr, &me, (cptr->flags & FLAGS_SENDQEX) ?
			    "SendQ exceeded" : "Dead socket");
	 i--;
	 continue;
      }

      killflag = NO;
      zkillflag = NO;

      if (rehashed) 
      {
	 if (zline_in_progress) 
	 {
	    if (IsPerson(cptr)) 
	    {
	       if ((aconf = find_zkill(cptr)))	
		  zkillflag = YES;
	    }
	 }
	 else 
	 {
	    if(IsPerson(cptr)) 
	    {
	       if((aconf = find_kill(cptr)))	
		  killflag = YES;	
	    }
	 }
      }

      /* Added a bit of code here to differentiate 
       * between K and Z-lines. -ThemBones
       */

      if (zkillflag || killflag)
      {
         ktype = zkillflag ? "Z-lined" : 
            ((aconf->status == CONF_KILL) ? "K-lined" : "Autokilled");

	 if (killflag) 
         {
	    sendto_ops("%s active for %s",
                       (aconf->status == CONF_KILL) ? "K-line" : "Autokill",
		       get_client_name(cptr, FALSE));
	    reason = aconf->passwd ? aconf->passwd : ktype;
	 }
	 else 
         {			/* its a Z line */
	    sendto_ops("Z-line active for %s",
		       get_client_name(cptr, FALSE));
	    reason = aconf->passwd ? aconf->passwd : "Z-lined";
	 }

	 sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP),
		    me.name, cptr->name, ktype);

         ircsprintf(fbuf, "%s: %s", ktype, reason);
	 (void) exit_client(cptr, cptr, &me, fbuf);
	 i--;			/* subtract out this fd so we check it again.. */			
	 continue;
      }

      if (IsRegistered(cptr))
	 ping = cptr->pingval;
      else
	 ping = CONNECTTIMEOUT;

      /*
       * Ok, so goto's are ugly and can be avoided here but this code
       * is already indented enough so I think its justified. -avalon
       *
       * justified by what? laziness? <g>
       * If the client pingtime is fine (ie, not larger than the client ping) 
       * skip over all the checks below. - lucas
       */

      if (ping < (currenttime - cptr->lasttime))
      {
         /*
          * If the server hasnt talked to us in 2*ping seconds and it has
          * a ping time, then close its connection. If the client is a
          * user and a KILL line was found to be active, close this
          * connection too.
          */
         if (((cptr->flags & FLAGS_PINGSENT) && ((currenttime - cptr->lasttime) >= (2 * ping))) ||
             ((!IsRegistered(cptr) && (currenttime - cptr->since) >= ping))) 
         {
	    if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr))) 
            {
	       if (cptr->authfd >= 0) 
	       {
	          (void) close(cptr->authfd);
	          cptr->authfd = -1;
	          cptr->count = 0;
	          *cptr->buffer = '\0';
	       }
#ifdef SHOW_HEADERS
	       if (DoingDNS(cptr))
	          ssl_send(cptr, REPORT_FAIL_DNS, R_fail_dns, 0);
	       if (DoingAuth(cptr))
	          ssl_send(cptr, REPORT_FAIL_ID, R_fail_id, 0);
#endif
	       Debug((DEBUG_NOTICE, "DNS/AUTH timeout %s",
	 	      get_client_name(cptr, TRUE)));
	       del_queries((char *) cptr);
	       ClearAuth(cptr);
	       ClearDNS(cptr);
	       SetAccess(cptr);
	       cptr->since = currenttime;
	       continue;
	    }

	    if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) 
	    {
	       ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
	       sendto_gnotice(fbuf, get_client_name(cptr, HIDEME));
	       ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);                                
	       sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME));
	    }

	    (void) exit_client(cptr, cptr, &me, "Ping timeout");
	    i--;			/* subtract out this fd so we check it again.. */			
	    continue;
         } /* don't send pings during a burst, as we send them already. */

         else if (!(cptr->flags & (FLAGS_PINGSENT|FLAGS_BURST))) {
	    /*
	     * if we havent PINGed the connection and we havent heard from
	     * it in a while, PING it to make sure it is still alive.
	     */
	    cptr->flags |= FLAGS_PINGSENT;
	    /*
	     * not nice but does the job 
	     */
	    cptr->lasttime = currenttime - ping;
	    sendto_one(cptr, "PING :%s", me.name);
         }
      }

      /* see EXPLANATION below
       *
       * timeout = cptr->lasttime + ping;
       * while (timeout <= currenttime)
       *  timeout += ping;
       * if (timeout < oldest || !oldest)
       *   oldest = timeout;
       */

      /*
       * Check UNKNOWN connections - if they have been in this state
       * for > 100s, close them.
       */
      if (IsUnknown(cptr))
	 if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0) 
	    (void) exit_client(cptr, cptr, &me, "Connection Timed Out");
   }

   rehashed = 0;
   zline_in_progress = 0;

   /* EXPLANATION
    * on a server with a large volume of clients, at any given point
    * there may be a client which needs to be pinged the next second,
    * or even right away (a second may have passed while running
    * check_pings). Preserving CPU time is more important than
    * pinging clients out at exact times, IMO. Therefore, I am going to make
    * check_pings always return currenttime + 9. This means that it may take
    * a user up to 9 seconds more than pingfreq to timeout. Oh well.
    * Plus, the number is 9 to 'stagger' our check_pings calls out over
    * time, to avoid doing it and the other tasks ircd does at the same time
    * all the time (which are usually done on intervals of 5 seconds or so). 
    * - lucas
    *
    *  if (!oldest || oldest < currenttime)
    *     oldest = currenttime + PINGFREQUENCY;
    */

   oldest = currenttime + 9;

   Debug((DEBUG_NOTICE, "Next check_ping() call at: %s, %d %d %d",
	  myctime(oldest), ping, oldest, currenttime));

   return oldest;
}
Пример #29
0
static	time_t	check_pings(time_t currenttime)
{
#ifdef TIMEDKLINES
	static	time_t	lkill = 0;
#endif
	Reg	aClient	*cptr;
	Reg	int	kflag = 0;
	aClient *bysptr = NULL;
	int	ping = 0, i;
	time_t	oldest = 0, timeout;
	char	*reason = NULL;

	for (i = highest_fd; i >= 0; i--)
	    {
		if (!(cptr = local[i]) || IsListener(cptr))
			continue;

#ifdef TIMEDKLINES
		kflag = 0;
		reason = NULL;
		/* 
		** Once per TIMEDKLINES seconds.
		** (1 minute is minimum resolution in K-line field)
		*/
		if ((currenttime - lkill > TIMEDKLINES)
			&& IsPerson(cptr) && !IsKlineExempt(cptr))
		{
			kflag = find_kill(cptr, 1, &reason);
		}
#endif
		ping = IsRegistered(cptr) ? cptr->ping : ACCEPTTIMEOUT;
		Debug((DEBUG_DEBUG, "c(%s) %d p %d k %d a %d",
			cptr->name, cptr->status, ping, kflag,
			currenttime - cptr->lasttime));
		/*
		 * Ok, so goto's are ugly and can be avoided here but this code
		 * is already indented enough so I think its justified. -avalon
		 */
		if (!kflag && IsRegistered(cptr) &&
		    (ping >= currenttime - cptr->lasttime))
			goto ping_timeout;
		/*
		 * If the server hasnt talked to us in 2*ping seconds
		 * and it has a ping time, then close its connection.
		 * If the client is a user and a KILL line was found
		 * to be active, close this connection too.
		 */
		if (kflag ||
		    ((currenttime - cptr->lasttime) >= (2 * ping) &&
		     (cptr->flags & FLAGS_PINGSENT)) ||
		    (!IsRegistered(cptr) &&
		     (currenttime - cptr->firsttime) >= ping))
		    {
			if (!IsRegistered(cptr) && 
			    (DoingDNS(cptr) || DoingAuth(cptr) ||
			     DoingXAuth(cptr)))
			    {
				if (cptr->authfd >= 0)
				    {
					(void)close(cptr->authfd);
					cptr->authfd = -1;
					cptr->count = 0;
					*cptr->buffer = '\0';
				    }
				Debug((DEBUG_NOTICE, "%s/%c%s timeout %s",
				       (DoingDNS(cptr)) ? "DNS" : "dns",
				       (DoingXAuth(cptr)) ? "X" : "x",
				       (DoingAuth(cptr)) ? "AUTH" : "auth",
				       get_client_name(cptr,TRUE)));
				del_queries((char *)cptr);
				ClearAuth(cptr);
#if defined(USE_IAUTH)
				if (DoingDNS(cptr) || DoingXAuth(cptr))
				    {
					if (DoingDNS(cptr) &&
					    (iauth_options & XOPT_EXTWAIT))
					    {
						/* iauth wants more time */
						sendto_iauth("%d d", cptr->fd);
						ClearDNS(cptr);
						cptr->lasttime = currenttime;
						continue;
					    }
					if (DoingXAuth(cptr) &&
					    (iauth_options & XOPT_NOTIMEOUT))
					    {
						cptr->exitc = EXITC_AUTHTOUT;
						sendto_iauth("%d T", cptr->fd);
						exit_client(cptr, cptr, &me,
						     "Authentication Timeout");
						continue;
					    }
					sendto_iauth("%d T", cptr->fd);
					SetDoneXAuth(cptr);
				    }
#endif
				ClearDNS(cptr);
				ClearXAuth(cptr);
				ClearWXAuth(cptr);
				cptr->firsttime = currenttime;
				cptr->lasttime = currenttime;
				continue;
			    }
			if (IsServer(cptr) || IsConnecting(cptr) ||
			    IsHandshake(cptr))
			{
				if (cptr->serv && cptr->serv->byuid[0])
				{
					bysptr = find_uid(cptr->serv->byuid,
							NULL);
				}
				/* we are interested only in *remote* opers */
				if (bysptr && !MyConnect(bysptr))
				{
					sendto_one(bysptr, ":%s NOTICE %s :"
						"No response from %s, closing"
						" link", ME, bysptr->name,
						get_client_name(cptr, FALSE));
				}
				sendto_flag(SCH_NOTICE,
					    "No response from %s closing link",
					    get_client_name(cptr, FALSE));
			}
			/*
			 * this is used for KILL lines with time restrictions
			 * on them - send a message to the user being killed
			 * first.
			 */
			if (kflag && IsPerson(cptr))
			    {
				char buf[100];

				sendto_flag(SCH_NOTICE,
					    "Kill line active for %s",
					    get_client_name(cptr, FALSE));
				cptr->exitc = EXITC_KLINE;
				if (!BadPtr(reason))
					sprintf(buf, "Kill line active: %.80s",
						reason);
				(void)exit_client(cptr, cptr, &me, (reason) ?
						  buf : "Kill line active");
			    }
			else
			    {
				cptr->exitc = EXITC_PING;
				(void)exit_client(cptr, cptr, &me,
						  "Ping timeout");
			    }
			continue;
		    }
		else if (IsRegistered(cptr) &&
			 (cptr->flags & FLAGS_PINGSENT) == 0)
		    {
			/*
			 * if we havent PINGed the connection and we havent
			 * heard from it in a while, PING it to make sure
			 * it is still alive.
			 */
			cptr->flags |= FLAGS_PINGSENT;
			/* not nice but does the job */
			cptr->lasttime = currenttime - ping;
			sendto_one(cptr, "PING :%s", me.name);
		    }
ping_timeout:
		timeout = cptr->lasttime + ping;
		while (timeout <= currenttime)
			timeout += ping;
		if (timeout < oldest || !oldest)
			oldest = timeout;
	    }
#ifdef TIMEDKLINES
	if (currenttime - lkill > 60)
		lkill = currenttime;
#endif
	if (!oldest || oldest < currenttime)
		oldest = currenttime + PINGFREQUENCY;
	if (oldest < currenttime + 30)
		oldest += 30;
	Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d",
		myctime(oldest), ping, oldest, currenttime));
	return (oldest);
}
Пример #30
0
/*
** DoNumeric (replacement for the old do_numeric)
**
**	parc	number of arguments ('sender' counted as one!)
**	parv[0]	pointer to 'sender' (may point to empty string) (not used)
**	parv[1]..parv[parc-1]
**		pointers to additional parameters, this is a NULL
**		terminated list (parv[parc] == NULL).
**
** *WARNING*
**	Numerics are mostly error reports. If there is something
**	wrong with the message, just *DROP* it! Don't even think of
**	sending back a neat error message -- big danger of creating
**	a ping pong error message...
*/
int  do_numeric(int numeric, aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	aClient *acptr;
	aChannel *chptr;
	char *nick, *p;
	int  i;

	/* Is this an outgoing connect, and we get a numeric 451 (not registered) back for the
	 * magic command __PANGPANG__ and we did not send a SERVER message yet?
	 * Then this means we are dealing with an Unreal server <3.2.9 and we should send the
	 * SERVER command right now.
	 */
	if (!IsServer(sptr) && !IsPerson(sptr) && (numeric == 451) && (parc > 2) && strstr(parv[1], "__PANGPANG__") &&
	    IsHandshake(cptr) && sptr->serv && !IsServerSent(sptr))
	{
		send_server_message(sptr);
		return 0;
	}

	if (parc < 1 || !IsServer(sptr))
		return 0;
	/* Remap low number numerics. */
	if (numeric < 100)
		numeric += 100;

	/*
	   ** Prepare the parameter portion of the message into 'buffer'.
	   ** (Because the buffer is twice as large as the message buffer
	   ** for the socket, no overflow can occur here... ...on current
	   ** assumptions--bets are off, if these are changed --msa)
	   ** Note: if buffer is non-empty, it will begin with SPACE.
	 */
	buffer[0] = '\0';
	if (parc > 2)
	{
		/*
		 * For strlcat nazis, please read above
		*/
		for (i = 2; i < (parc - 1); i++)
		{
			(void)strcat(buffer, " ");
			(void)strcat(buffer, parv[i]);
		}
		(void)strcat(buffer, " :");
		(void)strcat(buffer, parv[parc - 1]);
	}
	else
		sendto_realops("do_numeric( %i, %s, %s, %i, { %s, %s } )!",
		    numeric, cptr->name, sptr->name, parc,
		    parv[0], parv[1] ? parv[1] : "<null>");
	for (; (nick = strtoken(&p, parv[1], ",")); parv[1] = NULL)
	{
		if ((acptr = find_client(nick, (aClient *)NULL)))
		{
			/*
			   ** Drop to bit bucket if for me...
			   ** ...one might consider sendto_ops
			   ** here... --msa
			   ** And so it was done. -avalon
			   ** And regretted. Dont do it that way. Make sure
			   ** it goes only to non-servers. -avalon
			   ** Check added to make sure servers don't try to loop
			   ** with numerics which can happen with nick collisions.
			   ** - Avalon
			 */
			if (!IsMe(acptr) && IsPerson(acptr))
			{
				/* Added for .U3.2. drop remote 'You are not on
				   ** that channel', we should be synced anyway,
				   ** and this is an annoying message with TSpre7
				   ** still on the net; would result in numeric 442 for
				   ** every KICK... Can be removed when TSpre7 is gone.
				   ** --Run
				   if (numeric==ERR_NOTONCHANNEL) return 0;
				 */

				sendto_prefix_one(acptr, sptr, ":%s %d %s%s",
				    parv[0], numeric, nick, buffer);
			}
			else if (IsServer(acptr) && acptr->from != cptr)
				sendto_prefix_one(acptr, sptr, ":%s %d %s%s",
				    parv[0], numeric, nick, buffer);
		}
		else if ((acptr = find_server_quick(nick)))
		{
			if (!IsMe(acptr) && acptr->from != cptr)
				sendto_prefix_one(acptr, sptr, ":%s %d %s%s",
				    parv[0], numeric, nick, buffer);
		}
		else if ((chptr = find_channel(nick, (aChannel *)NULL)))
			sendto_channel_butone(cptr, sptr, chptr, ":%s %d %s%s",
			    parv[0], numeric, chptr->chname, buffer);
	}
	return 0;
}