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