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