@@ -4,6 +4,7 @@ var net = require('net'),
4
4
dns = require ( 'dns' ) ,
5
5
_ = require ( 'lodash' ) ,
6
6
winston = require ( 'winston' ) ,
7
+ crypto = require ( 'crypto' ) ,
7
8
Socks = require ( 'socksjs' ) ,
8
9
EventBinder = require ( './eventbinder.js' ) ,
9
10
IrcServer = require ( './server.js' ) ,
@@ -599,7 +600,7 @@ IrcConnection.prototype.setDefaultUserDetails = function () {
599
600
this . username = ( this . username || global . config . default_ident || '%n' )
600
601
. replace ( '%n' , ( this . nick . replace ( / [ ^ 0 - 9 a - z A - Z \- _ . \/ ] / , '' ) || 'nick' ) )
601
602
. replace ( '%h' , this . user . hostname )
602
- . replace ( '%i' , ip2Hex ( this . user . address ) || '00000000' ) ;
603
+ . replace ( '%i' , getIpIdent ( this . user . address ) ) ;
603
604
604
605
this . gecos = ( this . gecos || global . config . default_gecos || '%n' )
605
606
. replace ( '%n' , this . nick )
@@ -766,16 +767,24 @@ var socketConnectHandler = function () {
766
767
} ;
767
768
768
769
770
+ // Convert a IPv4-mapped IPv6 addresses to a regular IPv4 address
771
+ function unmapIPv4 ( address ) {
772
+ if ( address . toLowerCase ( ) . indexOf ( '::ffff:' ) == 0 ) {
773
+ address = address . substring ( 7 ) ;
774
+ }
775
+ return address
776
+ }
777
+
769
778
770
779
/**
771
780
* Load any WEBIRC or alternative settings for this connection
772
781
* Called in scope of the IrcConnection instance
773
782
*/
774
783
function findWebIrc ( connect_data ) {
775
784
var webirc_pass = global . config . webirc_pass ,
785
+ address = unmapIPv4 ( this . user . address ) ,
776
786
found_webirc_pass , tmp ;
777
787
778
-
779
788
// Do we have a single WEBIRC password?
780
789
if ( typeof webirc_pass === 'string' && webirc_pass ) {
781
790
found_webirc_pass = webirc_pass ;
@@ -788,7 +797,13 @@ function findWebIrc(connect_data) {
788
797
if ( found_webirc_pass ) {
789
798
// Build the WEBIRC line to be sent before IRC registration
790
799
tmp = 'WEBIRC ' + found_webirc_pass + ' KiwiIRC ' ;
791
- tmp += this . user . hostname + ' ' + this . user . address ;
800
+ var hostname = this . user . hostname ;
801
+ // Add a 0 in front of IP(v6) addresses starting with a colon
802
+ // (otherwise the colon will be interpreted as meaning that the
803
+ // rest of the line is a single argument).
804
+ if ( hostname [ 0 ] == ':' ) hostname = '0' + hostname ;
805
+ if ( address [ 0 ] == ':' ) address = '0' + address ;
806
+ tmp += hostname + ' ' + address ;
792
807
793
808
connect_data . prepend_data = [ tmp ] ;
794
809
}
@@ -865,25 +880,63 @@ function socketOnData(data) {
865
880
}
866
881
867
882
883
+ // Encodes a Buffer of bytes into an RFC 4648 base32 encoded string.
884
+ // Does not include padding.
885
+ // From https://github.com/agnoster/base32-js
886
+ function base32Encode ( input ) {
887
+ var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ;
888
+ var skip = 0 ; // How many bits we will skip from the first byte
889
+ var bits = 0 ; // 5 high bits, carry from one byte to the next
890
+ var output = '' ;
891
+
892
+ for ( var i = 0 ; i < input . length ; ) {
893
+ var byte = input [ i ] ;
894
+ if ( skip < 0 ) { // We have a carry from the previous byte
895
+ bits |= byte >> ( - skip ) ;
896
+ } else { // No carry
897
+ bits = ( byte << skip ) & 0xF8 ; // 0b11111000
898
+ }
868
899
869
- function ip2Hex ( ip ) {
870
- // We can only deal with IPv4 addresses for now
871
- if ( ! ip . match ( / ^ [ 0 - 9 ] { 0 , 3 } \. [ 0 - 9 ] { 0 , 3 } \. [ 0 - 9 ] { 0 , 3 } \. [ 0 - 9 ] { 0 , 3 } $ / ) ) {
872
- return ;
873
- }
874
-
875
- var hexed = ip . split ( '.' ) . map ( function ipSplitMapCb ( i ) {
876
- var hex = parseInt ( i , 10 ) . toString ( 16 ) ;
900
+ if ( skip > 3 ) {
901
+ // Not enough data to produce a character, get us another one
902
+ skip -= 8 ;
903
+ ++ i ;
904
+ continue ;
905
+ }
877
906
878
- // Pad out the hex value if it's a single char
879
- if ( hex . length === 1 ) {
880
- hex = '0' + hex ;
907
+ if ( skip < 4 ) {
908
+ // Produce a character
909
+ output += alphabet [ bits >> 3 ] ;
910
+ skip += 5 ;
881
911
}
912
+ }
913
+
914
+ return output + ( skip < 0 ? alphabet [ bits >> 3 ] : '' ) ;
915
+ }
882
916
883
- return hex ;
884
- } ) . join ( '' ) ;
885
917
886
- return hexed ;
918
+ function getIpIdent ( ip ) {
919
+ ip = unmapIPv4 ( ip ) ;
920
+ if ( ip . indexOf ( '.' ) != - 1 ) { // IPv4
921
+ // With IPv4 addresses we can just encode the address in hex
922
+ return ip . split ( '.' ) . map ( function ipSplitMapCb ( i , idx ) {
923
+ var hex = parseInt ( i , 10 ) . toString ( 16 ) ;
924
+
925
+ // Pad out the hex value if it's a single char
926
+ if ( hex . length === 1 )
927
+ hex = '0' + hex ;
928
+
929
+ return hex ;
930
+ } ) . join ( '' ) ;
931
+ } else { // IPv6
932
+ // Generate a hash of the IP address and encode it in base-32. This
933
+ // can have collisions, but the probability should be very low (about
934
+ // 1 / 32^x, where x is the max ident length). Why base32? Because
935
+ // the ident is compared case-insensitively in hostmasks and is used by
936
+ // humans who may confuse similar characters.
937
+ return base32Encode ( crypto . createHash ( 'sha1' )
938
+ . update ( ip ) . digest ( ) ) . substr ( 0 , 10 ) ;
939
+ }
887
940
}
888
941
889
942
0 commit comments