]> jfr.im git - irc/quakenet/snircd-patchqueue.git/blame - sethost.patch
entralizemodecccheck: use char table the ircd already has, mark there ctcp and contro...
[irc/quakenet/snircd-patchqueue.git] / sethost.patch
CommitLineData
0a17908b 1sethost patch: minor overhaul of sethost system
2
3this patch replaces a number of other patches:
4autosethost.patch invalidatebanssethost.patch issethost.patch
5sethostnewhostmask.patch sethostoldcode.patch sethostprotocolviolation.patch
6showumodehtoclients.patch ulined.patch
7
8makes usermode +h visible to clients
9
10removes HasSetHost() macro (was same as IsSetHost())
11
12removed 'Using S-line privilege' message on connect
13client is informed of spoofhost by 396 RPL_HOSTHIDDEN (host :is now your hidden host)
14
15change syntax to "SETHOST [<user>@]<host> [<password>]"
16
17disallow client to use MODE +h [<user>@]<host> [<pass>] (bug: mode +h took 2 parameters, even from remote users!)
18
19sethost can only be unset with "MODE <nick> -h"
20
21sethost #N no longer supported (N being the Nth configured spoof block)
22
23remote sethost can now be undone by using 0 as username and host,
24but only when the user has an account set and is allowed by server settings to set mode +x,
25to avoid revealing the real host out of the blue
26
27SETHOST now same as OPER
28modes h and o can only be set with SETHOST and OPER
29modes h and o are visible to clients
30modes h and o can only be unset with MODE <nick> -ho
31
32user sethosts are now also checked against their user@host/ip (and not just the password)
33
34applying the sethost and syncing of clients (quit user, rejoin user, restore modes on user) is now done in one place
35
36remote sethost can come from any server, does not need to have a Uworld block or be a service (ACCOUNT doesnt require that either)
37
38STATS s
39show if spoofhost is valid or not (S = valid, s = invalid)
40removed numbering (not required anymore?)
41merged showing of user@host
42
43diff -r c6f3803ee169 include/client.h
44--- a/include/client.h
45+++ b/include/client.h
46@@ -90,7 +90,7 @@
47 #define FlagClr(set,flag) ((set)->bits[FLAGSET_INDEX(flag)] &= ~FLAGSET_MASK(flag))
48
49 /** String containing valid user modes, in no particular order. */
50-#define infousermodes "dioOswkgxRXInP"
51+#define infousermodes "dioOswkghxRXInP"
52
53 /** Character to indicate no oper name available */
54 #define NOOPERNAMECHARACTER '-'
55@@ -627,7 +627,6 @@
56 #define HasHiddenHost(x) (IsHiddenHost(x) && IsAccount(x))
57 /** Return non-zero if the client is using a spoofhost */
58 #define IsSetHost(x) HasFlag(x, FLAG_SETHOST)
59-#define HasSetHost(x) (IsSetHost(x))
60
61 /** Mark a client as having an in-progress net.burst. */
62 #define SetBurst(x) SetFlag(x, FLAG_BURST)
34145209 63diff -r c6f3803ee169 include/ircd_chattr.h
64--- a/include/ircd_chattr.h
65+++ b/include/ircd_chattr.h
66@@ -117,6 +117,8 @@
67 #define IsUserChar(c) (IRCD_CharAttrTab[(c) - CHAR_MIN] & NTL_IRCUI)
68 /** Test whether a character is valid in a hostname. */
69 #define IsHostChar(c) (IRCD_CharAttrTab[(c) - CHAR_MIN] & NTL_IRCHN)
70+/** Test whether a character is valid in a sethost */
71+#define IsSetHostChar(c) (IRCD_CharAttrTab[(c) - CHAR_MIN] & (NTL_IRCHN|NTL_IRCIP6))
72 /** Test whether a character is valid in an IPv4 address. */
73 #define IsIPChar(c) (IRCD_CharAttrTab[(c) - CHAR_MIN] & NTL_IRCIP)
74 /** Test whether a character is valid in an IPv6 address. */
75@@ -127,4 +129,5 @@
76 #define IsKTimeChar(c) (IRCD_CharAttrTab[(c) - CHAR_MIN] & NTL_KTIME)
77
78
79+
80 #endif /* INCLUDED_ircd_chattr_h */
0a17908b 81diff -r c6f3803ee169 include/numeric.h
82--- a/include/numeric.h
83+++ b/include/numeric.h
84@@ -314,7 +314,7 @@
85 /* RPL_NOUSERS 395 Dalnet/EFnet/IRCnet */
86 #define RPL_HOSTHIDDEN 396 /* UMODE +x completed succesfuly */
87 #define RPL_STATSSLINE 398 /* QuakeNet extension -froo */
88-#define RPL_USINGSLINE 399 /* QuakeNet extension -froo */
89+/* RPL_USINGSLINE 399 QuakeNet extension -froo */
90
91 /*
92 * Errors are in the range from 400-599 currently and are grouped by what
93diff -r c6f3803ee169 include/s_conf.h
94--- a/include/s_conf.h
95+++ b/include/s_conf.h
96@@ -205,7 +205,8 @@
97
98 extern void conf_add_sline(const char* const* fields, int count);
99 extern void clear_slines(void);
100-extern int conf_check_slines(struct Client *cptr);
101+extern struct sline *find_spoofblock(struct Client *cptr, char *spoofhost, char *password);
102+extern int apply_spoofblock(struct Client *cptr);
103 extern void free_spoofhost(struct sline *spoof);
104
105 extern void yyerror(const char *msg);
106diff -r c6f3803ee169 include/s_user.h
107--- a/include/s_user.h
108+++ b/include/s_user.h
109@@ -79,15 +79,15 @@
110 extern int set_nick_name(struct Client* cptr, struct Client* sptr,
111 const char* nick, int parc, char* parv[]);
112 extern void send_umode_out(struct Client* cptr, struct Client* sptr,
113- struct Flags* old, int prop);
114+ struct Flags* old, int prop, int alreadyh);
115 extern int whisper(struct Client* source, const char* nick,
116 const char* channel, const char* text, int is_notice);
117 extern void send_user_info(struct Client* to, char* names, int rpl,
118 InfoFormatter fmt);
119
120 extern int hide_hostmask(struct Client *cptr, unsigned int flags);
121-extern int set_hostmask(struct Client *cptr, char *hostmask, char *password);
122-extern int is_hostmask(char *word);
123+extern int set_hostmask(struct Client *sptr, char *user, char *host);
04d03d60 124+extern int is_validsethost(char *user, char *host);
0a17908b 125 extern int set_user_mode(struct Client *cptr, struct Client *sptr,
126 int parc, char *parv[], int allow_modes);
127 extern int is_silenced(struct Client *sptr, struct Client *acptr);
128@@ -102,7 +102,7 @@
129 extern struct Client* next_client(struct Client* next, const char* ch);
130 extern char *umode_str(struct Client *cptr, int type);
131 extern void send_umode(struct Client *cptr, struct Client *sptr,
132- struct Flags *old, int sendset, int opernames);
133+ struct Flags *old, int sendset, int opernames, int alreadyh);
134 extern void set_snomask(struct Client *, unsigned int, int);
135 extern int is_snomask(char *);
136 extern int check_target_limit(struct Client *sptr, void *target, const char *name,
137diff -r c6f3803ee169 ircd/channel.c
138--- a/ircd/channel.c
139+++ b/ircd/channel.c
140@@ -384,12 +384,12 @@
141 ircd_ntoa_r(iphost, &cli_ip(cptr));
142
143 /* sr is real host if +h */
144- if (HasSetHost(cptr))
145+ if (IsSetHost(cptr))
146 sr = cli_user(cptr)->realhost;
147
148 /* if +x and not +h sa is real host, if -x or +h sa is the account host */
149 if (IsAccount(cptr)) {
150- if (HasHiddenHost(cptr) && !HasSetHost(cptr)) {
151+ if (HasHiddenHost(cptr) && !IsSetHost(cptr)) {
152 sa = cli_user(cptr)->realhost;
153 } else {
154 ircd_snprintf(0, tmphost, HOSTLEN, "%s.%s",
155diff -r c6f3803ee169 ircd/m_oper.c
156--- a/ircd/m_oper.c
157+++ b/ircd/m_oper.c
158@@ -189,7 +189,7 @@
159
160 set_snomask(sptr, SNO_OPERDEFAULT, SNO_ADD);
161 cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */
162- send_umode_out(cptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE));
163+ send_umode_out(cptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE), 0);
164 send_reply(sptr, RPL_YOUREOPER);
165
166 sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) is now operator (%c) as %s",
167diff -r c6f3803ee169 ircd/m_sethost.c
168--- a/ircd/m_sethost.c
169+++ b/ircd/m_sethost.c
9ba0fa7c 170@@ -82,174 +82,225 @@
0a17908b 171 #include "config.h"
172
173 #include "client.h"
174+#include "ircd_features.h"
175+#include "ircd_log.h"
176 #include "ircd_reply.h"
177 #include "ircd_string.h"
178 #include "ircd_snprintf.h"
179-#include "ircd_features.h"
180+#include "msg.h"
181 #include "msgq.h"
182 #include "numeric.h"
183 #include "s_conf.h"
184+#include "s_debug.h"
185 #include "s_user.h"
186-#include "s_debug.h"
187 #include "send.h"
188 #include "struct.h"
189 #include "numnicks.h"
190
191-#include "channel.h"
192-#include "msg.h"
193-
194-#include <assert.h>
195 #include <stdlib.h>
196
197-/*
198+/**
199 * m_sethost - generic message handler
200 *
201- * mimic old lain syntax:
202+ * SETHOST [<user>@]<host> [<password>]
203 *
204- * (Oper) /SETHOST ident host.cc [quit-message]
205- * (User) /SETHOST host.cc password
206- * (Both) /SETHOST undo
207+ * parv[0] = sender prefix
208+ * parv[1] = [user@]host
209+ * parv[2] = password
210 *
211- * check for undo, prepend parv w. <nick> -h or +h
212+ * "MODE <nick> -h" to remove sethost
213+ *
214 */
215 int m_sethost(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
216 {
217- char hostmask[USERLEN + HOSTLEN + 2];
218- char curhostmask[USERLEN + HOSTLEN + 2];
219+ int alreadyh = 0;
220+ int freeform = 0;
221+ char *hostmask;
222+ char *user = NULL;
223+ char *host;
224+ char *password = NULL;
225+ struct Flags setflags;
226+ struct sline *sconf;
227
228- struct Flags setflags;
229+ /* disabled */
230+ if (!feature_bool(FEAT_SETHOST))
231+ return send_reply(sptr, ERR_DISABLED, "SETHOST");
232
233- /* Back up the flags first */
234+ /* disabled for ordinary users */
235+ if (!IsAnOper(sptr) && !feature_bool(FEAT_SETHOST_USER))
236+ return send_reply(sptr, ERR_NOPRIVILEGES);
237+
238+ /* need hostmask parameter
239+ * and need password parameter from an ordinary user
240+ */
241+ if (parc < 2 || EmptyString(parv[1]) || (parc < 3 && !IsAnOper(sptr)))
242+ return need_more_params(sptr, "SETHOST");
243+
244+ hostmask = parv[1];
245+
246+ /* get user and host */
247+ if ((host = strrchr(hostmask, '@'))) {
248+ *host++ = '\0';
249+ user = hostmask;
250+ }
251+ else
252+ host = hostmask;
253+
254+ /* got a pasword */
255+ if (parc > 2 && !EmptyString(parv[2]))
256+ password = parv[2];
257+
258+ /* freeform - do not bother with password */
259+ if (IsAnOper(sptr) && HasPriv(sptr, PRIV_FREEFORM)) {
260+ freeform = 1;
261+ password = NULL;
262+ }
263+
264+ /* check if user and host are valid */
04d03d60 265+ if (!is_validsethost(user, host))
0a17908b 266+ return send_reply(sptr, ERR_BADHOSTMASK, user ? user : "", user ? "@" : "", host);
267+
268+ /* not freeform */
269+ if (!freeform) {
270+
271+ /* find the spoof block */
272+ if (!(sconf = find_spoofblock(sptr, host, password)))
273+ return send_reply(sptr, ERR_HOSTUNAVAIL, user ? user : "", user ? "@" : "", host);
274+ else
275+ host = (char *)sconf->spoofhost;
276+
277+ /* only freeform allowed to specify user */
278+ user = NULL;
279+ }
280+
281+ /* backup flags */
282 setflags = cli_flags(sptr);
283
284- if (parc < 2)
285- return need_more_params(sptr, "SETHOST");
286+ /* already +h, clear flag to force mode +h to be sent out again */
287+ if (IsSetHost(sptr)) {
288+ FlagClr(&setflags, FLAG_SETHOST);
289+ alreadyh = 1;
290+ }
291
292- if (0 == ircd_strcmp("undo", parv[1])) {
293- set_hostmask(sptr, NULL, NULL);
294- } else {
295- if (parc<3)
296- return need_more_params(sptr, "SETHOST");
297- if (IsAnOper(sptr)) {
298- ircd_snprintf(0, hostmask, USERLEN + HOSTLEN + 2, "%s@%s", parv[1], parv[2]);
299- if (!is_hostmask(hostmask)) {
300- send_reply(sptr, ERR_BADHOSTMASK, hostmask);
301- return 0;
302- }
303- if (IsSetHost(sptr) || IsAccount(sptr)) {
304- ircd_snprintf(0, curhostmask, USERLEN + HOSTLEN + 2, "%s@%s", sptr->cli_user->username, sptr->cli_user->host);
305- if (0 == strcmp(hostmask, curhostmask)) {
306- send_reply(sptr, RPL_HOSTHIDDEN, curhostmask);
307- return 0;
308- }
309- }
310- if (set_hostmask(sptr, hostmask, NULL))
311- FlagClr(&setflags, FLAG_SETHOST);
312- } else {
313- if (!is_hostmask(parv[1])) {
314- send_reply(sptr, ERR_BADHOSTMASK, parv[1]);
315- return 0;
316- }
317- if (IsSetHost(sptr) || IsAccount(sptr)) {
318- if (0 == strcmp(parv[1], sptr->cli_user->host)) {
319- send_reply(sptr, RPL_HOSTHIDDEN, parv[1]);
320- return 0;
321- }
322- }
323- if (set_hostmask(sptr, parv[1], parv[2]))
324- FlagClr(&setflags, FLAG_SETHOST);
325- }
326- }
327+ /* check if new sethost is different from before */
328+ if (IsSetHost(sptr) &&
329+ (!user || strcmp(cli_user(sptr)->username, user) == 0) &&
330+ strcmp(cli_user(sptr)->host, host) == 0)
331+ return send_reply(sptr, RPL_HOSTHIDDEN, user ? user : "", user ? "@" : "", host);
332
333- send_umode_out(cptr, sptr, &setflags, 0);
334+ /* do it */
335+ set_hostmask(sptr, user, host);
336+
337+ /* log freeform */
9ba0fa7c 338+ if (freeform) {
339+
340+ sendto_opmask_butone(0, SNO_OLDSNO,
341+ "%C SETHOST %s@%s (freeform)", sptr, user, host);
342+
343+ log_write(LS_SETHOST, L_NOTICE, LOG_NOSNOTICE,
344+ "%s SETHOST %s@%s (freeform)", get_client_name(sptr, SHOW_IP), user, host);
345+ }
0a17908b 346+
347+ /* send the mode out */
348+ send_umode_out(cptr, sptr, &setflags, 0, alreadyh);
349+
350 return 0;
351 }
352
353
354-/*
355+
356+/**
357 * ms_sethost - sethost server message handler
358 *
359 * parv[0] = sender prefix
360 * parv[1] = target user numeric
361- * parv[2] = target user's new ident
362- * parv[3] = target user's new host
363+ * parv[2] = spoof username
364+ * parv[3] = spoof host
365+ *
366+ * undo sethost when spoof username and host are 0
367+ *
368 */
369+ /* TODO: IsRemoteSetHost() */
370 int ms_sethost(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
371 {
372- struct Client *target;
373- char hostmask[USERLEN + HOSTLEN + 2];
374- struct Membership *chan;
375+ int alreadyh = 0;
376+ char *target;
377+ char *user;
378+ char *host;
379+ struct Client *acptr;
380 struct Flags setflags;
381
71300e6b 382+ /* not from a server */
383+ if (!IsServer(sptr))
384+ return protocol_violation(cptr, "Received SETHOST from user %C", sptr);
385+
0a17908b 386+ /* check paramaters */
387 if (parc < 4)
71300e6b 388- return need_more_params(sptr, "SETHOST");
389+ return protocol_violation(cptr, "Received too few parameters for SETHOST from %C (got %d - need 4)", sptr, parc);
0a17908b 390
71300e6b 391- if (!IsServer(sptr))
392- return protocol_violation(cptr, "SETHOST from non-server %s",
393- cli_name(sptr));
0a17908b 394+ target = parv[1];
395+ user = parv[2];
396+ host = parv[3];
0a17908b 397
398- /* Locate our target user; ignore the message if we can't */
399- if(!(target = findNUser(parv[1])))
400+ /* find user */
401+ if(!(acptr = findNUser(target)))
402 return 0;
403
404- /* Fake host assignments must be from services */
405- if (!find_conf_byhost(cli_confs(sptr), cli_name(sptr), CONF_UWORLD))
406- return protocol_violation(cptr, "Non-U:lined server %s set fake host on user %s", cli_name(sptr), cli_name(target));
407-
408- if (!MyConnect(target)) {
409- sendcmdto_one(sptr, CMD_SETHOST, cli_user(target)->server, "%C %s %s", target,
410- parv[2], parv[3]);
411+ /* not for my user, pass it along */
412+ if (!MyConnect(acptr)) {
413+ sendcmdto_one(sptr, CMD_SETHOST, acptr, "%C %s %s", acptr, user, host);
414 return 0;
415 }
416
417- /* Back up the flags first */
418- setflags = cli_flags(target);
419- FlagClr(&setflags, FLAG_SETHOST);
420+ /* backup flags */
421+ setflags = cli_flags(acptr);
422
423- if (IsSetHost(target) || IsAccount(target)) {
424- if ((0 == strcmp(parv[2], target->cli_user->username)) && (0 == strcmp(parv[3], target->cli_user->host)))
425+ /* check user and host are valid */
04d03d60 426+ if (!is_validsethost(user, host))
0a17908b 427+ return protocol_violation(cptr,
71300e6b 428+ "Received SETHOST for %C with invalid user host '%s %s' from %C",
429+ acptr, user, host, sptr);
0a17908b 430+
431+ /* 'user host' is '0 0' - undo sethost */
432+ if (user[0] == '0' && user[1] == '\0' &&
433+ host[0] == '0' && host[1] == '\0') {
434+
435+ /* user has no sethost or has no account
436+ *
437+ * user has +h their host is hidden, do not remove it
438+ * unless the user has an account set
439+ * we should not out of the blue expose the real host
440+ */
441+ if (!IsSetHost(acptr) || !IsAccount(acptr))
442 return 0;
443+
444+ /* user not +x and not allowed to set it */
445+ if (!IsHiddenHost(acptr) && !feature_bool(FEAT_HOST_HIDING))
446+ return 0;
447+
448+ /* set +x */
449+ SetHiddenHost(acptr);
450+ user = NULL;
451+ host = NULL;
452 }
453
454- ircd_snprintf(0, hostmask, USERLEN + HOSTLEN + 2, "%s@%s", parv[2], parv[3]);
455- if (!is_hostmask(hostmask))
456- return protocol_violation(cptr, "Bad Host mask %s for user %s", hostmask, cli_name(target));
457+ /* check if new sethost is different from before */
458+ else if (IsSetHost(acptr) &&
459+ strcmp(cli_user(acptr)->username, user) == 0 &&
460+ strcmp(cli_user(acptr)->host, host) == 0)
461+ return 0;
462
463- sendcmdto_common_channels_butone(target, CMD_QUIT, target, ":Host change");
464-
465- /* Assign and propagate the fakehost */
466- SetSetHost(target);
467- ircd_strncpy(cli_user(target)->username, parv[2], USERLEN);
468- ircd_strncpy(cli_user(target)->host, parv[3], HOSTLEN);
469-
470- send_reply(target, RPL_HOSTHIDDEN, hostmask);
471-
472- /*
473- * Go through all channels the client was on, rejoin him
474- * and set the modes, if any
475- */
476- for (chan = cli_user(target)->channel; chan; chan = chan->next_channel) {
477- if (IsZombie(chan))
478- continue;
479- /* If this channel has delayed joins and the user has no modes, just set
480- * the delayed join flag rather than showing the join, even if the user
481- * was visible before */
482- if (!IsChanOp(chan) && !HasVoice(chan)
483- && (chan->channel->mode.mode & MODE_DELJOINS)) {
484- SetDelayedJoin(chan);
485- } else {
486- sendcmdto_channel_butserv_butone(target, CMD_JOIN, chan->channel, target, 0,
487- "%H", chan->channel);
488- }
489- if (IsChanOp(chan) && HasVoice(chan)) {
490- sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, target, 0,
491- "%H +ov %C %C", chan->channel, target, target);
492- } else if (IsChanOp(chan) || HasVoice(chan)) {
493- sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, target, 0,
494- "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', target);
495- }
496+ /* already +h, clear flag to force mode +h to be sent out again */
497+ else if (IsSetHost(acptr)) {
498+ FlagClr(&setflags, FLAG_SETHOST);
499+ alreadyh = 1;
500 }
501
502- send_umode_out(target, target, &setflags, 0);
503+ /* do it */
504+ set_hostmask(acptr, user, host);
505+
506+ /* send out the mode */
507+ send_umode_out(acptr, acptr, &setflags, 0, alreadyh);
508+
509 return 0;
510 }
511diff -r c6f3803ee169 ircd/m_userhost.c
512--- a/ircd/m_userhost.c
513+++ b/ircd/m_userhost.c
514@@ -104,7 +104,7 @@
515 * of +x. If an oper wants the real host, he should go to
516 * /whois to get it.
517 */
518- (HasHiddenHost(cptr) || HasSetHost(cptr)) && (sptr != cptr) ?
519+ (HasHiddenHost(cptr) || IsSetHost(cptr)) && (sptr != cptr) ?
520 cli_user(cptr)->host : cli_user(cptr)->realhost);
521 }
522
523diff -r c6f3803ee169 ircd/m_userip.c
524--- a/ircd/m_userip.c
525+++ b/ircd/m_userip.c
526@@ -106,7 +106,7 @@
527 * of +x. If an oper wants the real IP, he should go to
528 * /whois to get it.
529 */
530- ((HasHiddenHost(cptr) || HasSetHost(cptr) || feature_bool(FEAT_HIS_USERIP)) && (sptr != cptr)) ?
531+ ((HasHiddenHost(cptr) || IsSetHost(cptr) || feature_bool(FEAT_HIS_USERIP)) && (sptr != cptr)) ?
532 feature_str(FEAT_HIDDEN_IP) :
533 ircd_ntoa(&cli_ip(cptr)));
534 }
535diff -r c6f3803ee169 ircd/m_who.c
536--- a/ircd/m_who.c
537+++ b/ircd/m_who.c
538@@ -394,14 +394,14 @@
539 && ((!(matchsel & WHO_FIELD_HOS))
540 || matchexec(cli_user(acptr)->host, mymask, minlen))
541 && ((!(matchsel & WHO_FIELD_HOS))
542- || !HasSetHost(acptr)
543+ || !IsSetHost(acptr)
544 || !HasHiddenHost(acptr)
545 || !IsAnOper(sptr)
546 || matchexec(cli_user(acptr)->realhost, mymask, minlen))
547 && ((!(matchsel & WHO_FIELD_REN))
548 || matchexec(cli_info(acptr), mymask, minlen))
549 && ((!(matchsel & WHO_FIELD_NIP))
550- || ((HasHiddenHost(acptr) || HasSetHost(acptr)) && !IsAnOper(sptr))
551+ || ((HasHiddenHost(acptr) || IsSetHost(acptr)) && !IsAnOper(sptr))
552 || !ipmask_check(&cli_ip(acptr), &imask, ibits))
553 && ((!(matchsel & WHO_FIELD_ACC))
554 || matchexec(cli_user(acptr)->account, mymask, minlen)))
555@@ -433,14 +433,14 @@
556 && ((!(matchsel & WHO_FIELD_HOS))
557 || matchexec(cli_user(acptr)->host, mymask, minlen))
558 && ((!(matchsel & WHO_FIELD_HOS))
559- || !HasSetHost(acptr)
560+ || !IsSetHost(acptr)
561 || !HasHiddenHost(acptr)
562 || !IsAnOper(sptr)
563 || matchexec(cli_user(acptr)->realhost, mymask, minlen))
564 && ((!(matchsel & WHO_FIELD_REN))
565 || matchexec(cli_info(acptr), mymask, minlen))
566 && ((!(matchsel & WHO_FIELD_NIP))
567- || ((HasHiddenHost(acptr) || HasSetHost(acptr)) && !IsAnOper(sptr))
568+ || ((HasHiddenHost(acptr) || IsSetHost(acptr)) && !IsAnOper(sptr))
569 || !ipmask_check(&cli_ip(acptr), &imask, ibits))
570 && ((!(matchsel & WHO_FIELD_ACC))
571 || matchexec(cli_user(acptr)->account, mymask, minlen)))
572diff -r c6f3803ee169 ircd/m_whois.c
573--- a/ircd/m_whois.c
574+++ b/ircd/m_whois.c
575@@ -214,7 +214,7 @@
576 if (IsAccount(acptr))
577 send_reply(sptr, RPL_WHOISACCOUNT, name, user->account);
578
579- if ((HasHiddenHost(acptr) || HasSetHost(acptr)) && ((IsAnOper(sptr) && HasPriv(sptr, PRIV_USER_PRIVACY)) || acptr == sptr))
580+ if ((HasHiddenHost(acptr) || IsSetHost(acptr)) && ((IsAnOper(sptr) && HasPriv(sptr, PRIV_USER_PRIVACY)) || acptr == sptr))
581 send_reply(sptr, RPL_WHOISACTUALLY, name, user->realusername,
582 user->realhost, ircd_ntoa(&cli_ip(acptr)));
583
584diff -r c6f3803ee169 ircd/s_conf.c
585--- a/ircd/s_conf.c
586+++ b/ircd/s_conf.c
587@@ -52,6 +52,7 @@
588 #include "s_bsd.h"
589 #include "s_debug.h"
590 #include "s_misc.h"
591+#include "s_user.h"
592 #include "send.h"
593 #include "struct.h"
594 #include "sys.h"
9ba0fa7c 595@@ -1239,44 +1240,159 @@
0a17908b 596 * -froo 1/2003
597 *
598 */
599+/**
600+ * find_spoofblock
601+ *
602+ * Find matching spoof block for a user for the given spoofhost and password
603+ *
604+ * @param cptr User wanting to get a spoof host.
605+ * @param spoofhost Spoof host to look for.
606+ * @param password Password given by user (can be NULL).
607+ *
608+ * @return pointer to the matching spoofblock is found, else NULL
609+ *
610+ */
611+struct sline *find_spoofblock(struct Client *cptr, char *spoofhost, char *password) {
9ba0fa7c 612
613+ char *error[4];
0a17908b 614+ struct sline *sconf;
615+ int result = 0;
616+ int r = 0;
9ba0fa7c 617+
618+ /* set error messages */
619+ error[0] = "no such Spoof block";
620+ error[1] = "IP / host mismatch";
621+ error[2] = "username mismatch";
622+ error[3] = password ? "password mismatch" : "password required";
623+
0a17908b 624+ Debug((DEBUG_INFO, "find_spoofblock() cptr=%C spoofhost=%s password=%s",
625+ cptr, spoofhost, password));
626+
627+ for (sconf = GlobalSList; sconf; sconf = sconf->next) {
628+
629+ /* check result of previous loop */
630+ if (r > result)
631+ result = r;
632+
633+ /* check spoofhost */
634+ if (strcasecmp(sconf->spoofhost, spoofhost) != 0)
635+ continue;
636+ r = 1;
637+
638+ /* check cptr's host */
639+ /* cidr mask */
640+ if (sconf->flags == SLINE_FLAGS_IP) {
641+ if (!ipmask_check(&(cli_ip(cptr)), &(sconf->address), sconf->bits))
642+ continue;
643+ }
644+
645+ /* hostname */
646+ else if (sconf->flags == SLINE_FLAGS_HOSTNAME) {
647+ if ((match(sconf->realhost, cli_sockhost(cptr)) != 0) &&
648+ (match(sconf->realhost, cli_sock_ip(cptr)) != 0)) /* wildcarded IP address */
649+ continue;
650+ }
651+
652+ /* not cidr or hostname.. */
653+ else
654+ continue;
655+ r = 2;
656+
657+ /* check cptr's username */
658+ if (!EmptyString(sconf->username) && match(sconf->username, cli_user(cptr)->realusername) != 0)
659+ continue;
660+ r = 3;
661+
662+ /* check password */
663+ if (password) {
664+ if (EmptyString(sconf->passwd) || strcmp(sconf->passwd, password) != 0)
665+ continue;
666+ }
667+
668+ /* no password, but need one for this spoofblock */
669+ else if (!EmptyString(sconf->passwd))
670+ continue;
671+
672+ /* got one */
9ba0fa7c 673+ log_write(LS_SETHOST, L_INFO, 0, "%s SETHOST %s (ok)",
674+ get_client_name(cptr, SHOW_IP), sconf->spoofhost);
0a17908b 675+ return sconf;
676+ }
677+
678+ /* TODO: lookup LOG stuff */
679+ /* TODO: L_INFO LOG_NOSNOTICE */
9ba0fa7c 680+ /* log best result we got */
681+ log_write(LS_SETHOST, L_INFO, 0, "%s SETHOST %s (%s)",
682+ get_client_name(cptr, SHOW_IP), spoofhost, error[result]);
0a17908b 683+ return NULL;
684+}
685+
686+
687+
688+/**
689+ * apply_spoofblock
690+ *
691+ * @param cptr User to apply Spoof block to on connect
692+ *
693+ * @return 1 for success, else 0
694+ *
695+ */
696 int
697-conf_check_slines(struct Client *cptr)
698+apply_spoofblock(struct Client *cptr)
699 {
700 struct sline *sconf;
701 char *hostonly;
702
703+ /* disabled */
704+ if(!feature_bool(FEAT_SETHOST_AUTO))
705+ return 0;
706+
707+ /* go over spoof blocks */
708 for (sconf = GlobalSList; sconf; sconf = sconf->next) {
709+
710+ /* check IP */
711 if (sconf->flags == SLINE_FLAGS_IP) {
712 if (!ipmask_check(&(cli_ip(cptr)), &(sconf->address), sconf->bits))
713 continue;
714+
715+ /* check host */
716 } else if (sconf->flags == SLINE_FLAGS_HOSTNAME) {
717 if ((match(sconf->realhost, cli_sockhost(cptr)) != 0) &&
718- (match(sconf->realhost, cli_sock_ip(cptr)) != 0)) /* wildcarded IP address */
719+ (match(sconf->realhost, cli_sock_ip(cptr)) != 0)) /* wildcarded IP address */
720 continue;
721- } else {
722- continue;
723- }
724+ } else
725+ continue;
726
727- if (match(sconf->username, cli_user(cptr)->username) == 0) {
728- /* Ignore user part if u@h. */
729- if ((hostonly = strchr(sconf->spoofhost, '@')))
730- hostonly++;
731- else
732- hostonly = sconf->spoofhost;
733+ /* check username */
734+ if (match(sconf->username, cli_user(cptr)->username) != 0)
735+ continue;
736
737- if(!*hostonly)
738- continue;
739+ /* Ignore user part if u@h. */
740+ if ((hostonly = strchr(sconf->spoofhost, '@')))
741+ hostonly++;
742+ else
743+ hostonly = sconf->spoofhost;
744
745- ircd_strncpy(cli_user(cptr)->host, hostonly, HOSTLEN);
746- log_write(LS_USER, L_INFO, LOG_NOSNOTICE, "S-Line (%s@%s) by (%#R)",
747- cli_user(cptr)->username, hostonly, cptr);
748- return 1;
749- }
750+ /* invalid */
04d03d60 751+ if (!is_validsethost(NULL, hostonly))
0a17908b 752+ continue;
753+
754+ /* do it and log */
755+ set_hostmask(cptr, NULL, hostonly);
756+ /* LOG_NOSNOTICE */
757+ log_write(LS_USER, L_INFO, 0, "AUTO SETHOST %s on %s",
758+ hostonly, get_client_name(cptr, SHOW_IP));
759+ return 1;
760 }
761 return 0;
762 }
763
764+
765+
766+/**
767+ *
768+ *
769+ */
770 void free_spoofhost(struct sline *spoof) {
771 MyFree(spoof->spoofhost);
772 MyFree(spoof->passwd);
773diff -r c6f3803ee169 ircd/s_err.c
774--- a/ircd/s_err.c
775+++ b/ircd/s_err.c
776@@ -824,13 +824,13 @@
777 /* 395 */
778 { 0 },
779 /* 396 */
780- { RPL_HOSTHIDDEN, "%s :is now your hidden host", "396" },
781+ { RPL_HOSTHIDDEN, "%s%s%s :is now your hidden host", "396" },
782 /* 397 */
783 { 0 },
784 /* 398 */
785- { RPL_STATSSLINE, "%d %s %s %s %s", "398" },
786+ { RPL_STATSSLINE, "%c %s %s %s%s%s", "398" },
787 /* 399 */
788- { RPL_USINGSLINE, ":Using S-line privilege", "399" },
789+ { 0 },
790 /* 400 */
791 { 0 },
792 /* 401 */
793@@ -1092,9 +1092,9 @@
794 /* 529 */
795 { 0 },
796 /* 530 */
797- { ERR_BADHOSTMASK, "%s :Invalid username/hostmask", "530" },
798+ { ERR_BADHOSTMASK, "%s%s%s :Invalid username/hostmask", "530" },
799 /* 531 */
800- { ERR_HOSTUNAVAIL, "%s :sethost not found", "531" },
801+ { ERR_HOSTUNAVAIL, "%s%s%s :Sethost not found", "531" },
802 /* 532 */
803 { 0 },
804 /* 533 */
805diff -r c6f3803ee169 ircd/s_stats.c
806--- a/ircd/s_stats.c
807+++ b/ircd/s_stats.c
808@@ -400,41 +400,37 @@
809 }
810 }
811
812+/* TODO: */
813+/** List spoof blocks.
814+ * @param[in] to Client requesting statistics.
815+ * @param[in] sd Stats descriptor for request (ignored).
816+ * @param[in] param Filter for spoofhost names.
817+ */
818 static void
819 stats_sline(struct Client* to, const struct StatDesc* sd, char* param)
820 {
821- int y = 1, i = 1;
822 struct sline *sline;
823
824 if (IsAnOper(to))
825- send_reply(to, SND_EXPLICIT | RPL_TEXT, "# Type Spoofhost Realhost Ident");
826+ send_reply(to, SND_EXPLICIT | RPL_TEXT, "S Type Spoofhost Hostmask");
827 else
828- send_reply(to, SND_EXPLICIT | RPL_TEXT, "# Type Spoofhost");
829+ send_reply(to, SND_EXPLICIT | RPL_TEXT, "S Type Spoofhost");
830
831 for (sline = GlobalSList; sline; sline = sline->next) {
832- if (param && match(param, sline->spoofhost)) { /* narrow search */
833- if (IsAnOper(to))
834- y++;
835- else
836- if (!EmptyString(sline->passwd))
837- y++;
838+
839+ if (param && match(param, sline->spoofhost))
840 continue;
841- }
842
843- if (IsAnOper(to)) {
844- send_reply(to, RPL_STATSSLINE, (param) ? y : i,
845- (EmptyString(sline->passwd)) ? "oper" : "user",
846- sline->spoofhost,
847- (EmptyString(sline->realhost)) ? "" : sline->realhost,
848- (EmptyString(sline->username)) ? "" : sline->username);
849- i++;
850- } else {
851- if (!EmptyString(sline->passwd)) {
852- send_reply(to, RPL_STATSSLINE, (param) ? y : i, "user", sline->spoofhost,
853- "", "", "");
854- i++;
855- }
856- }
857+ if (IsAnOper(to))
858+ send_reply(to, RPL_STATSSLINE,
04d03d60 859+ is_validsethost(NULL, sline->spoofhost) ? 'S' : 's', /* valid show S else s */
0a17908b 860+ (EmptyString(sline->passwd)) ? "Oper" : "User",
861+ sline->spoofhost,
862+ (EmptyString(sline->username)) ? "" : sline->username,
863+ (!EmptyString(sline->username)) ? "@" : "", /* always place a @ after the username */
864+ (EmptyString(sline->realhost)) ? "" : sline->realhost);
04d03d60 865+ else if (!EmptyString(sline->passwd) && is_validsethost(NULL, sline->spoofhost))
0a17908b 866+ send_reply(to, RPL_STATSSLINE, 'S', "User", sline->spoofhost, "", "", "");
867 }
868 }
869
870diff -r c6f3803ee169 ircd/s_user.c
871--- a/ircd/s_user.c
872+++ b/ircd/s_user.c
873@@ -73,9 +73,6 @@
874 #include <string.h>
875 #include <sys/stat.h>
876
877-static char *IsVhost(char *hostmask, int oper);
878-static char *IsVhostPass(char *hostmask);
879-
880 /** Count of allocated User structures. */
881 static int userCount = 0;
882
883@@ -373,13 +370,6 @@
884
885 if (feature_bool(FEAT_AUTOINVISIBLE))
886 SetInvisible(sptr);
887-
888- if(feature_bool(FEAT_SETHOST_AUTO)) {
889- if (conf_check_slines(sptr)) {
890- send_reply(sptr, RPL_USINGSLINE);
891- SetSetHost(sptr);
892- }
893- }
894
895 SetUser(sptr);
896 cli_handler(sptr) = CLIENT_HANDLER;
897@@ -411,6 +401,10 @@
898 cli_info(sptr), NumNick(cptr) /* two %s's */);
899
900 IPcheck_connect_succeeded(sptr);
901+
902+ /* TODO: */
903+ /* apply auto sethost if needed */
904+ apply_spoofblock(sptr);
905 }
906 else {
907 struct Client *acptr = user->server;
908@@ -519,7 +513,7 @@
909 else
910 FlagClr(&flags, FLAG_ACCOUNT);
911 client_set_privs(sptr, NULL);
912- send_umode(cptr, sptr, &flags, ALL_UMODES, 0);
913+ send_umode(cptr, sptr, &flags, ALL_UMODES, 0, 0);
914 if ((cli_snomask(sptr) != SNO_DEFAULT) && HasFlag(sptr, FLAG_SERVNOTICE))
915 send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr));
916 }
917@@ -874,14 +868,15 @@
918 * @param[in] sptr Client who sent us the mode change message.
919 * @param[in] old Prior set of user flags.
920 * @param[in] prop If non-zero, also include FLAG_OPER.
921+ * @param[in] alreadyh Client is already +h, do not show +h change
922 */
923 void send_umode_out(struct Client *cptr, struct Client *sptr,
924- struct Flags *old, int prop)
925+ struct Flags *old, int prop, int alreadyh)
926 {
927 int i;
928 struct Client *acptr;
929
930- send_umode(NULL, sptr, old, prop ? SEND_UMODES : SEND_UMODES_BUT_OPER, 0);
931+ send_umode(NULL, sptr, old, prop ? SEND_UMODES : SEND_UMODES_BUT_OPER, 0, 0);
932
933 for (i = HighestFd; i >= 0; i--)
934 {
935@@ -890,7 +885,7 @@
936 sendcmdto_one(sptr, CMD_MODE, acptr, "%s %s", cli_name(sptr), umodeBuf);
937 }
938
939- send_umode(NULL, sptr, old, prop ? SEND_UMODES : SEND_UMODES_BUT_OPER, 1);
940+ send_umode(NULL, sptr, old, prop ? SEND_UMODES : SEND_UMODES_BUT_OPER, 1, 0);
941
942 for (i = HighestFd; i >= 0; i--)
943 {
944@@ -900,7 +895,7 @@
945 }
946
947 if (cptr && MyUser(cptr))
948- send_umode(cptr, sptr, old, ALL_UMODES, 0);
949+ send_umode(cptr, sptr, old, ALL_UMODES, 0, alreadyh);
950 }
951
952
953@@ -965,7 +960,7 @@
954 }
955
956 SetFlag(cptr, flag);
957- if (!HasFlag(cptr, FLAG_HIDDENHOST) || !HasFlag(cptr, FLAG_ACCOUNT) || HasSetHost(cptr))
958+ if (!HasFlag(cptr, FLAG_HIDDENHOST) || !HasFlag(cptr, FLAG_ACCOUNT) || IsSetHost(cptr))
959 return 0;
960
961 sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Registered");
962@@ -974,7 +969,7 @@
963
964 /* ok, the client is now fully hidden, so let them know -- hikari */
965 if (MyConnect(cptr))
966- send_reply(cptr, RPL_HOSTHIDDEN, cli_user(cptr)->host);
967+ send_reply(cptr, RPL_HOSTHIDDEN, "", "", cli_user(cptr)->host);
968
969 /*
970 * Go through all channels the client was on, rejoin him
971@@ -999,201 +994,104 @@
972 return 0;
973 }
974
975+/* TODO: */
976 /*
977 * set_hostmask() - derived from hide_hostmask()
978 *
979 */
980-int set_hostmask(struct Client *cptr, char *hostmask, char *password)
981+int set_hostmask(struct Client *cptr, char *user, char *host)
982 {
983- int restore = 0;
984- int freeform = 0;
985- char *host, *new_vhost, *vhost_pass;
986- char hiddenhost[USERLEN + HOSTLEN + 2];
987+
988+ int userchange = 0;
989+ char hiddenhost[USERLEN + 1 + HOSTLEN + 1];
990+ char *msg = "Host change";
991 struct Membership *chan;
992
993- Debug((DEBUG_INFO, "set_hostmask() %C, %s, %s", cptr, hostmask, password));
994+ assert(0 != cptr);
995
996- /* sethost enabled? */
997- if (MyConnect(cptr) && !feature_bool(FEAT_SETHOST)) {
998- send_reply(cptr, ERR_DISABLED, "SETHOST");
999+ Debug((DEBUG_INFO, "set_hostmask() cptr=%C user=%s host=%s",
1000+ cptr, user ? user : "<null>", host ? host : "<null>"));
1001+
1002+ /* remove sethost, but user has none */
1003+ if (!host && !IsSetHost(cptr))
1004 return 0;
1005+
1006+ /* remove sethost, user has +x host, realusername and username are the same
1007+ * pretend the user just set +x
1008+ */
1009+ if (!host && HasHiddenHost(cptr) &&
1010+ strcmp(cli_user(cptr)->username, cli_user(cptr)->realusername) == 0)
1011+ msg = "Registered";
1012+
1013+ /* quit user */
1014+ sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":%s", msg);
1015+
1016+ /* remove sethost */
1017+ if (!host) {
1018+
1019+ /* clear flag */
1020+ ClearSetHost(cptr);
1021+
1022+ /* restore user and host */
1023+ if (HasHiddenHost(cptr))
1024+ ircd_snprintf(0, cli_user(cptr)->host, HOSTLEN, "%s.%s",
1025+ cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
1026+ else
1027+ strncpy(cli_user(cptr)->host, cli_user(cptr)->realhost, HOSTLEN);
1028+ if (MyConnect(cptr) && strcmp(cli_user(cptr)->username, cli_user(cptr)->realusername) != 0)
1029+ userchange = 1;
1030+ strncpy(cli_user(cptr)->username, cli_user(cptr)->realusername, USERLEN);
1031 }
1032
1033- /* sethost enabled for users? */
1034- if (MyConnect(cptr) && !IsAnOper(cptr) && !feature_bool(FEAT_SETHOST_USER)) {
1035- send_reply(cptr, ERR_NOPRIVILEGES);
1036- return 0;
1037- }
1038-
1039- /* MODE_DEL: restore original hostmask */
1040- if (EmptyString(hostmask)) {
1041- /* is already sethost'ed? and only opers can remove a sethost */
1042- if (IsSetHost(cptr) && IsAnOper(cptr)) {
1043- restore = 1;
1044- sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
1045- /* If they are +rx, we need to return to their +x host, not their "real" host */
1046- if (HasHiddenHost(cptr))
1047- ircd_snprintf(0, cli_user(cptr)->host, HOSTLEN, "%s.%s",
1048- cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
1049- else
1050- strncpy(cli_user(cptr)->host, cli_user(cptr)->realhost, HOSTLEN);
1051- strncpy(cli_user(cptr)->username, cli_user(cptr)->realusername, USERLEN);
1052- /* log it */
1053- if (MyConnect(cptr))
1054- log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE,
1055- "SETHOST (%s@%s) by (%#R): restoring real hostmask",
1056- cli_user(cptr)->username, cli_user(cptr)->host, cptr);
1057- } else
1058- return 0;
1059- /* MODE_ADD: set a new hostmask */
1060- } else {
1061- /* chop up ident and host.cc */
1062- if ((host = strrchr(hostmask, '@'))) { /* oper can specifiy ident@host.cc */
1063- *host++ = '\0';
1064- if ( MyConnect(cptr) && (0 == strcmp(host, cli_user(cptr)->host)) && (0 == strcmp(hostmask, cli_user(cptr)->username))) {
1065- ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
1066- cli_user(cptr)->username, cli_user(cptr)->host);
1067- send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost);
1068- return 0;
1069- }
1070- } else { /* user can only specifiy host.cc [password] */
1071- host = hostmask;
1072- if ( MyConnect(cptr) && (0 == strcmp(host, cli_user(cptr)->host))) {
1073- ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
1074- cli_user(cptr)->username, cli_user(cptr)->host);
1075- send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost);
1076- return 0;
1077- }
1078- }
1079- /*
1080- * Oper sethost
1081- */
1082- if (MyConnect(cptr)) {
1083- if (IsAnOper(cptr)) {
1084- if ((new_vhost = IsVhost(host, 1)) == NULL) {
1085- if (!HasPriv(cptr, PRIV_FREEFORM)) {
1086- send_reply(cptr, ERR_HOSTUNAVAIL, hostmask);
1087- log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE,
1088- "SETHOST (%s@%s) by (%#R): no such s-line",
1089- (host != hostmask) ? hostmask : cli_user(cptr)->username, host, cptr);
1090- return 0;
1091- } else /* freeform active, log and go */
1092- freeform = 1;
1093- }
1094- sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
1095- /* set the new ident and host */
1096- if (host != hostmask) /* oper only specified host.cc */
1097- strncpy(cli_user(cptr)->username, hostmask, USERLEN);
1098- strncpy(cli_user(cptr)->host, host, HOSTLEN);
1099- /* log it */
1100- log_write(LS_SETHOST, (freeform) ? L_NOTICE : L_INFO,
1101- (freeform) ? 0 : LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)%s",
1102- cli_user(cptr)->username, cli_user(cptr)->host, cptr,
1103- (freeform) ? ": using freeform" : "");
1104- /*
1105- * plain user sethost, handled here
1106- */
1107- } else {
1108- /* empty password? */
1109- if (EmptyString(password)) {
1110- send_reply(cptr, ERR_NEEDMOREPARAMS, "MODE");
1111- return 0;
1112- }
1113- /* no such s-line */
1114- if ((new_vhost = IsVhost(host, 0)) == NULL) {
1115- send_reply(cptr, ERR_HOSTUNAVAIL, hostmask);
1116- log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s %s) by (%#R): no such s-line",
1117- cli_user(cptr)->username, host, password, cptr);
1118- return 0;
1119- }
1120- /* no password */
1121- if ((vhost_pass = IsVhostPass(new_vhost)) == NULL) {
1122- send_reply(cptr, ERR_PASSWDMISMATCH);
1123- log_write(LS_SETHOST, L_INFO, 0, "SETHOST (%s@%s %s) by (%#R): trying to use an oper s-line",
1124- cli_user(cptr)->username, host, password, cptr);
1125- return 0;
1126- }
1127- /* incorrect password */
1128- if (strCasediff(vhost_pass, password)) {
1129- send_reply(cptr, ERR_PASSWDMISMATCH);
1130- log_write(LS_SETHOST, L_NOTICE, 0, "SETHOST (%s@%s %s) by (%#R): incorrect password",
1131- cli_user(cptr)->username, host, password, cptr);
1132- return 0;
1133- }
1134- sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
1135- /* set the new host */
1136- strncpy(cli_user(cptr)->host, new_vhost, HOSTLEN);
1137- /* log it */
1138- log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)",
1139- cli_user(cptr)->username, cli_user(cptr)->host, cptr);
1140- }
1141- } else { /* remote user */
1142- sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change");
1143- if (host != hostmask) /* oper only specified host.cc */
1144- strncpy(cli_user(cptr)->username, hostmask, USERLEN);
1145- strncpy(cli_user(cptr)->host, host, HOSTLEN);
1146- }
1147+ /* apply sethost */
1148+ else {
1149+
1150+ /* set flag */
1151+ SetSetHost(cptr);
1152+
1153+ /* update user and host */
1154+ if (user)
1155+ strncpy(cli_user(cptr)->username, user, USERLEN);
1156+ strncpy(cli_user(cptr)->host, host, HOSTLEN);
1157 }
1158
1159- if (restore)
1160- ClearSetHost(cptr);
1161- else
1162- SetSetHost(cptr);
1163+ /* tell user */
1164+ if (MyConnect(cptr)) {
1165
1166- if (MyConnect(cptr)) {
1167- ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s",
1168- cli_user(cptr)->username, cli_user(cptr)->host);
1169- send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost);
1170+ /* user and host changed */
1171+ if (userchange || strcmp(cli_user(cptr)->username, cli_user(cptr)->realusername) != 0)
1172+ send_reply(cptr, RPL_HOSTHIDDEN, cli_user(cptr)->username, "@", cli_user(cptr)->host);
1173+
1174+ /* just host changed */
1175+ else
1176+ send_reply(cptr, RPL_HOSTHIDDEN, "", "", cli_user(cptr)->host);
1177 }
1178
1179-#if 0
1180- /* Code copied from hide_hostmask(). This is the old (pre-delayedjoin)
1181- * version. Switch this in if you're not using the delayed join patch. */
1182- /*
1183- * Go through all channels the client was on, rejoin him
1184- * and set the modes, if any
1185- */
1186+ /* go over the channels */
1187 for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) {
1188+
1189+ /* invalidate bans so they are rechecked */
1190+ ClearBanValid(chan);
1191+
1192+ /* zombie */
1193 if (IsZombie(chan))
1194 continue;
1195- sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr,
1196- "%H", chan->channel);
1197- if (IsChanOp(chan) && HasVoice(chan)) {
1198- sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr,
1199- "%H +ov %C %C", chan->channel, cptr, cptr);
1200- } else if (IsChanOp(chan) || HasVoice(chan)) {
1201- sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr,
1202- "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr);
1203- }
1204- }
1205-#endif
1206
1207- /*
1208- * Go through all channels the client was on, rejoin him
1209- * and set the modes, if any
1210- */
1211- for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) {
1212- if (IsZombie(chan))
1213- continue;
1214- /* If this channel has delayed joins and the user has no modes, just set
1215- * the delayed join flag rather than showing the join, even if the user
1216- * was visible before */
1217- if (!IsChanOp(chan) && !HasVoice(chan)
1218- && (chan->channel->mode.mode & MODE_DELJOINS)) {
1219- SetDelayedJoin(chan);
1220- } else {
1221+ /* not delayed join, rejoin user to chan */
1222+ if (!IsDelayedJoin(chan))
1223 sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, 0,
1224 "%H", chan->channel);
1225- }
1226- if (IsChanOp(chan) && HasVoice(chan)) {
1227+
1228+ /* restore modes */
1229+ if (IsChanOp(chan) && HasVoice(chan))
1230 sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0,
1231 "%H +ov %C %C", chan->channel, cptr, cptr);
1232- } else if (IsChanOp(chan) || HasVoice(chan)) {
1233+ else if (IsChanOp(chan) || HasVoice(chan))
1234 sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0,
1235 "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr);
1236- }
1237 }
1238- return 1;
1239+
1240+ return 0;
1241 }
1242
1243 /** Set a user's mode. This function checks that \a cptr is trying to
1244@@ -1220,15 +1118,17 @@
1245 unsigned int tmpmask = 0;
1246 int snomask_given = 0;
1247 char buf[BUFSIZE];
1248- char *hostmask, *password;
1249+ char *hostmask = NULL;
1250 int prop = 0;
1251 int do_host_hiding = 0;
1252 int do_set_host = 0;
1253+ int alreadyh = 0;
1254 size_t opernamelen;
1255 char *opername = 0;
1256 char* account = NULL;
1257+ char *user = NULL;
1258+ char *host = NULL;
1259
1260- hostmask = password = NULL;
1261 what = MODE_ADD;
1262
1263 if (parc < 3)
1264@@ -1238,8 +1138,7 @@
1265 for (i = 0; i < USERMODELIST_SIZE; i++)
1266 {
1267 if (HasFlag(sptr, userModeList[i].flag) &&
1268- ((userModeList[i].flag != FLAG_ACCOUNT) &&
1269- (userModeList[i].flag != FLAG_SETHOST)))
1270+ userModeList[i].flag != FLAG_ACCOUNT)
1271 *m++ = userModeList[i].c;
1272 }
1273 *m = '\0';
1274@@ -1251,6 +1150,9 @@
1275 return 0;
1276 }
1277
1278+ if (IsSetHost(sptr))
1279+ alreadyh = 1;
1280+
1281 /*
1282 * find flags already set for user
1283 * why not just copy them?
1284@@ -1394,26 +1296,18 @@
1285 break;
1286 case 'h':
1287 if (what == MODE_ADD) {
1288- if (*(p + 1) && is_hostmask(*(p + 1))) {
1289- do_set_host = 1;
1290- hostmask = *++p;
1291- /* DON'T step p onto the trailing NULL in the parameter array! - splidge */
1292- if (*(p+1))
1293- password = *++p;
1294- else
1295- password = NULL;
1296- } else {
1297- if (!*(p+1))
1298- send_reply(sptr, ERR_NEEDMOREPARAMS, "SETHOST");
1299+ /* TODO: */
1300+ if (IsServer(cptr)) {
1301+ if (!*(p + 1))
71300e6b 1302+ protocol_violation(cptr, "Received MODE +h for %C without sethost parameter", sptr);
0a17908b 1303 else {
1304- send_reply(sptr, ERR_BADHOSTMASK, *(p+1));
1305- p++; /* Swallow the arg anyway */
1306+ hostmask = *++p;
1307+ do_set_host = 1;
1308 }
1309 }
1310 } else { /* MODE_DEL */
1311+ hostmask = NULL;
1312 do_set_host = 1;
1313- hostmask = NULL;
1314- password = NULL;
1315 }
1316 break;
1317 case 'R':
1318@@ -1468,6 +1362,11 @@
1319 if (!FlagHas(&setflags, FLAG_PARANOID) && !(IsOper(sptr) && HasPriv(sptr, PRIV_PARANOID)))
1320 ClearParanoid(sptr);
1321
1322+ /* TODO: */
1323+ /* only opers can remove a sethost */
1324+ if (do_set_host && !hostmask && !FlagHas(&setflags, FLAG_LOCOP) && !FlagHas(&setflags, FLAG_OPER))
1325+ do_set_host = 0;
1326+
1327 /*
1328 * only send wallops to opers
1329 */
77cf570e 1330@@ -1521,11 +1420,38 @@
0a17908b 1331 }
1332 if (!FlagHas(&setflags, FLAG_HIDDENHOST) && do_host_hiding && allow_modes != ALLOWMODES_DEFAULT)
1333 hide_hostmask(sptr, FLAG_HIDDENHOST);
1334+
1335+ /* TODO: */
1336 if (do_set_host) {
1337- /* We clear the flag in the old mask, so that the +h will be sent */
1338- /* Only do this if we're SETTING +h and it succeeded */
1339- if (set_hostmask(sptr, hostmask, password) && hostmask)
1340- FlagClr(&setflags, FLAG_SETHOST);
1341+
1342+ /* mode -h */
1343+ if (!hostmask)
1344+ set_hostmask(sptr, NULL, NULL);
1345+
1346+ /* mode +h */
1347+ else {
1348+ if ((host = strrchr(hostmask, '@'))) {
1349+ *host++ = '\0';
1350+ user = hostmask;
1351+ }
1352+ else
1353+ host = hostmask;
1354+
77cf570e 1355+ /* dont check if sethost from remote users is valid with is_validsethost(),
1356+ * do check that user and host are not emtpy and
04d03d60 1357+ * do check if they start with a : as things go horribly wrong then
1358+ */
77cf570e 1359+ if (*user == 0 || user[0] == ':' || *host == 0 || host[0] == ':')
71300e6b 1360+ protocol_violation(cptr, "Received MODE +h for %C with an invalid user@host '%s@%s'",
0a17908b 1361+ sptr, user ? user : "", host);
1362+
1363+ /* apply it */
1364+ else {
1365+ /* clear flag in old mask so that +h will be sent again */
1366+ FlagClr(&setflags, FLAG_SETHOST);
1367+ set_hostmask(sptr, user, host);
1368+ }
1369+ }
1370 }
1371
1372 if (IsRegistered(sptr)) {
77cf570e 1373@@ -1577,7 +1503,7 @@
0a17908b 1374 }
1375 assert(UserStats.opers <= UserStats.clients + UserStats.unknowns);
1376 assert(UserStats.inv_clients <= UserStats.clients + UserStats.unknowns);
1377- send_umode_out(cptr, sptr, &setflags, prop);
1378+ send_umode_out(cptr, sptr, &setflags, prop, alreadyh);
1379 }
1380
1381 return 0;
77cf570e 1382@@ -1652,9 +1578,11 @@
0a17908b 1383 * @param[in] old Pre-change set of modes for \a sptr.
1384 * @param[in] sendset One of ALL_UMODES, SEND_UMODES_BUT_OPER,
1385 * SEND_UMODES, to select which changed user modes to send.
1386+ * @param[in] opernames Include opername parameter.
1387+ * @param[in] alreadyh Client already has +h set, do not show +h change.
1388 */
1389 void send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old,
1390- int sendset, int opernames)
1391+ int sendset, int opernames, int alreadyh)
1392 {
1393 int i;
1394 int flag;
77cf570e 1395@@ -1697,12 +1625,15 @@
0a17908b 1396 }
1397 /* Special case for SETHOST.. */
1398 if (flag == FLAG_SETHOST) {
1399- /* Don't send to users */
1400- if (cptr && MyUser(cptr))
1401- continue;
1402-
1403- /* If we're setting +h, add the parameter later */
1404- if (!FlagHas(old, flag))
1405+
1406+ /* do not show +h if client already had it */
1407+ if (cptr && MyUser(cptr) && IsSetHost(cptr) && alreadyh)
1408+ continue;
1409+
1410+ /* If we're setting +h, add the parameter later,
1411+ * but not when showing to the user
1412+ */
1413+ if (!FlagHas(old, flag) && (!cptr || !MyUser(cptr)))
1414 needhost++;
1415 }
1416 if (FlagHas(old, flag))
77cf570e 1417@@ -1741,7 +1672,7 @@
6fb35586 1418 }
1419 if (needhost) {
1420 *m++ = ' ';
1421- ircd_snprintf(0, m, USERLEN + HOSTLEN + 1, "%s@%s", cli_user(sptr)->username,
1422+ ircd_snprintf(0, m, USERLEN + HOSTLEN + 2, "%s@%s", cli_user(sptr)->username,
1423 cli_user(sptr)->host);
1424 } else
1425 *m = '\0';
34145209 1426@@ -1769,108 +1700,53 @@
0a17908b 1427 return 0;
1428 }
1429
1430- /*
1431- * Check to see if it resembles a valid hostmask.
1432- */
1433-int is_hostmask(char *word)
04d03d60 1434+/* TODO: */
0a17908b 1435+/**
1436+ * Check to see if it resembles a valid sethost.
1437+ *
04d03d60 1438+ * @param[in] user Username to check (can be NULL)
1439+ * @param[in] host Hostname to check
1440+ * @return Non-zero if user and host look valid for a sethost
0a17908b 1441+ *
1442+ */
04d03d60 1443+int is_validsethost(char *user, char *host)
0a17908b 1444 {
1445- int i = 0;
1446- char *host;
34145209 1447+ int i; /* loop variable */
0a17908b 1448
1449- Debug((DEBUG_INFO, "is_hostmask() %s", word));
04d03d60 1450+ assert(host != NULL);
1451
0a17908b 1452- if (strlen(word) > (HOSTLEN + USERLEN + 1) || strlen(word) <= 0)
34145209 1453+ /* must not be empty, not longer than HOSTLEN, not start with a : */
04d03d60 1454+ if (*host == 0 || strlen(host) > HOSTLEN || host[0] == ':')
0a17908b 1455 return 0;
1456
1457- /* if a host is specified, make sure it's valid */
1458- host = strrchr(word, '@');
1459- if (host) {
1460- if (strlen(++host) < 1)
1461- return 0;
1462- if (strlen(host) > HOSTLEN)
1463- return 0;
04d03d60 1464+ /* got a user part */
1465+ if (user) {
1466+
34145209 1467+ /* must not be empty, not longer than USERLEN, not start with a : */
1468+ if (*user == 0 || strlen(user) > USERLEN || user[0] == ':')
0a17908b 1469+ return 0;
04d03d60 1470+
34145209 1471+ /* check user chars
1472+ * use IsSetHostChar instead of IsUserChar
04d03d60 1473+ * as the latter allows a lot more chars
1474+ * we dont want in a sethost
1475+ */
1476+ for (i = 0; user[i]; i++) {
34145209 1477+ if (!IsSetHostChar(user[i]))
04d03d60 1478+ return 0;
1479+ }
0a17908b 1480 }
1481
1482- if (word) {
1483- if ('@' == *word) /* no leading @'s */
1484- return 0;
1485-
1486- if ('#' == *word) { /* numeric index given? */
1487- for (word++; *word; word++) {
1488- if (!IsDigit(*word))
1489- return 0;
1490- }
1491- return 1;
1492- }
1493-
1494- /* normal hostmask, account for at most one '@' */
1495- for (; *word; word++) {
1496- if ('@' == *word) {
1497- i++;
1498- continue;
1499- }
1500- if (!IsHostChar(*word))
1501- return 0;
1502- }
1503- return (1 < i) ? 0 : 1; /* no more than on '@' */
1504- }
1505- return 0;
1506-}
1507-
1508- /*
1509- * IsVhost() - Check if given host is a valid spoofhost
1510- * (ie: configured thru a S:line)
1511- */
1512-static char *IsVhost(char *hostmask, int oper)
1513-{
1514- unsigned int i = 0, y = 0;
1515- struct sline *sconf;
1516-
1517- Debug((DEBUG_INFO, "IsVhost() %s", hostmask));
1518-
1519- if (EmptyString(hostmask))
1520- return NULL;
1521-
1522- /* spoofhost specified as index, ie: #27 */
1523- if ('#' == hostmask[0]) {
1524- y = atoi(hostmask + 1);
1525- for (i = 0, sconf = GlobalSList; sconf; sconf = sconf->next) {
1526- if (!oper && EmptyString(sconf->passwd))
1527- continue;
1528- if (y == ++i)
1529- return sconf->spoofhost;
1530- }
1531- return NULL;
34145209 1532+ /* check host chars
1533+ * IsSetHostChar allows -. 0-9 A-Z [\]^_ a-Z {|} ~ and :
1534+ */
04d03d60 1535+ for (i = 0; host[i]; i++) {
34145209 1536+ if (!IsSetHostChar(host[i]))
04d03d60 1537+ return 0;
1538 }
1539
0a17908b 1540- /* spoofhost specified as host, ie: host.cc */
1541- for (sconf = GlobalSList; sconf; sconf = sconf->next)
1542- if (strCasediff(hostmask, sconf->spoofhost) == 0)
1543- return sconf->spoofhost;
34145209 1544-
0a17908b 1545- return NULL;
1546-}
34145209 1547-
0a17908b 1548- /*
1549- * IsVhostPass() - Check if given spoofhost has a password
1550- * associated with it, and if, return the password (cleartext)
1551- */
1552-static char *IsVhostPass(char *hostmask)
1553-{
1554- struct sline *sconf;
1555-
1556- Debug((DEBUG_INFO, "IsVhostPass() %s", hostmask));
1557-
1558- if (EmptyString(hostmask))
1559- return NULL;
1560-
1561- for (sconf = GlobalSList; sconf; sconf = sconf->next)
1562- if (strCasediff(hostmask, sconf->spoofhost) == 0) {
1563- Debug((DEBUG_INFO, "sconf->passwd %s", sconf->passwd));
1564- return EmptyString(sconf->passwd) ? NULL : sconf->passwd;
1565- }
1566-
1567- return NULL;
34145209 1568+ /* valid */
0a17908b 1569+ return 1;
1570 }
1571
1572 /** Update snomask \a oldmask according to \a arg and \a what.
1573diff -r c6f3803ee169 ircd/send.c
1574--- a/ircd/send.c
1575+++ b/ircd/send.c
1576@@ -281,7 +281,7 @@
1577 {
1578 case MATCH_HOST:
1579 return (match(mask, cli_user(one)->host) == 0 ||
1580- ((HasHiddenHost(one) || HasSetHost(one)) && match(mask, cli_user(one)->realhost) == 0));
1581+ ((HasHiddenHost(one) || IsSetHost(one)) && match(mask, cli_user(one)->realhost) == 0));
1582 case MATCH_SERVER:
1583 default:
1584 return (match(mask, cli_name(cli_user(one)->server)) == 0);
1585diff -r c6f3803ee169 ircd/whocmds.c
1586--- a/ircd/whocmds.c
1587+++ b/ircd/whocmds.c
1588@@ -134,7 +134,7 @@
1589
1590 if (fields & WHO_FIELD_NIP)
1591 {
1592- const char* p2 = (HasHiddenHost(acptr) || HasSetHost(acptr) || feature_bool(FEAT_HIS_USERIP)) && (!IsAnOper(sptr) || (IsAnOper(sptr) && !HasPriv(sptr, PRIV_USER_PRIVACY))) ?
1593+ const char* p2 = (HasHiddenHost(acptr) || IsSetHost(acptr) || feature_bool(FEAT_HIS_USERIP)) && (!IsAnOper(sptr) || (IsAnOper(sptr) && !HasPriv(sptr, PRIV_USER_PRIVACY))) ?
1594 feature_str(FEAT_HIDDEN_IP) :
1595 ircd_ntoa(&cli_ip(acptr));
1596 *(p1++) = ' ';
1597@@ -210,7 +210,7 @@
1598 *(p1++) = 'w';
1599 if (SendDebug(acptr))
1600 *(p1++) = 'g';
1601- if (HasSetHost(acptr))
1602+ if (IsSetHost(acptr))
1603 *(p1++) = 'h';
1604 }
1605 if (HasHiddenHost(acptr))