This document contains various technical information geared towards developers of microdc. === User connection identification improvements: There are four possible cases to consider: We initiating | They initiating ================================================= !+RCTM (we passive) | %+RCTM => we initiating CTM %+CTM (we active) | -CTM (R)CTM = (Rev)ConnectToMe - = We cannot antipicate !+ = We can anticipate by pending_userinfo and we_connected=true %+ = We can anticipate by pending_userinfo and we_connected=false Current implementation has pending_userinfo. To be more accurate, we should split this map into pending_userinfo_{in,out}. validate_nick need to be modified to check based on uc->we_connected. RevConnectToMe vs ConnectToMe considerations: 1) Security consideration: We don't want to connect to other users. It's the one that connects that starts sending data. The remote user could make us connect and send data ($MyNick+$Lock) to anyone on the net, which is bad. 2) Some users may think they are active, but they are not. The following is only relevant for active microdc users: These two considerations gives that it is better to be connected to than connect to, i.e. it is better to send ConnectToMe's and receive RevConnectToMe's rather than to send RevConnectToMe's and receive ConnectToMe's. So here's one an improved scheme for response to remote connection initiations. 1. Receive ConnectToMe from remote. 2. Ignore ConnectToMe from remote, and send ConnectToMe to remote. 3.1. If remote is not running microdc, remote will now connect to us. 3.2. If remote is running microdc, remote will send ConnectToMe to us again. 4.2. We connect to the remote. Problems: * Hubs may block repeated ConnectToMe's. * Clients' behavior on receiving ConnectToMe right after sending ConnectToMe could be unexpected. * This scheme is unfair to other clients not implementing this scheme. User active/passive state transition At the moment, there are four values for DCActiveState (DCUserInfo.active_state). Initial state is UNKNOWN. Note. KNOWN_ACTIVE only means that the user states that he is active. It means that the user's client has active enabled. But the client may be misconfigured, or the user's firewall may be. Note. RECEIVED_PASSIVE doesn't mean that the user states that he is passive. It only means that the user sent us $RevConnectToMe. It is unclear whether the client is forbidden to send $RevConnectToMe in active mode. UNKNOWN set on SENT_ACTIVE in main.c:DC_MSG_VALIDATE_NICK:ok # make way for future ConnectToMe KNOWN_ACTIVE set on SENT_PASSIVE in main.c:DC_MSG_VALIDATE_NICK:ok # logically! RECEIVED_PASSIVE set on * in hub.c:hub_handle_command:$RevConnectToMe # naturally! tested for in hub.c:hub_handle_command:$RevConnectToMe:!is_active # do we need to send back $RevConnectToMe? tested for in hub.c:hub_connect_user:!is_active # user also passive SENT_PASSIVE tested for in hub.c:hub_handle_command:$RevConnectToMe:!is_active # cannot communicate? tested for in hub.c:hub_connect_user:!is_active # waiting - RevConnectToMe already sent set on !RECEIVED_PASSIVE in hub.c:hub_connect_user:!is_active SENT_ACTIVE tested for in hub.c:hub_connect_user:is_active # waiting - ConnectToMe already sent set on * in hub.c:hub_connect_user:is_active When passive: UNKNOWN => SENT_PASSIVE => RECEIVED_PASSIVE [DC++ clients] UNKNOWN => SENT_PASSIVE [non-DC++ clients] UNKNOWN => SENT_PASSIVE => KNOWN_ACTIVE => SENT_PASSIVE => .. UNKNOWN => RECEIVED_PASSIVE When active: UNKNOWN => RECEIVED_PASSIVE => SENT_ACTIVE => UNKNOWN => .. UNKNOWN => SENT_ACTIVE => UNKNOWN => .. When user sends new $MyINFO RECEIVED_PASSIVE => UNKNOWN KNOWN_ACTIVE => UNKNOWN SENT_PASSIVE unchanged - technically we could try resending this. XXX? SENT_ACTIVE unchanged - will always work Problem: Loss of KNOWN_ACTIVE state We lose KNOWN_ACTIVE knowledge if 1) we are passive (!is_active) 2) we have connected to remote user, and state has become KNOWN_ACTIVE 3) we try to connect to remote user again by sending $RevConnectToMe State becomes SENT_PASSIVE. Problem: Loss of RECEIVED_PASSIVE state Assume we are passive. State for some user is RECEIVED_PASSIVE. We then become active (setting changed). RECEIVED_PASSIVE information is when we try to connect to user next time. Since passive DC++ clients only sends RevConnectToMe back once in reply to $RevConnectToMe, it means we might get stuck in SENT_PASSIVE state. This is not a problem though. Relevant states in microdc active mode: all but SENT_ACTIVE Relevant states in microdc passive mode: all but SEND_PASSIVE When changing from active to passive, we must only consider SENT_ACTIVE state. OK : SENT_ACTIVE tested for in hub.c:hub_connect_user:is_active -- is_active is false! When changing from passive to active, we must only consider SENT_PASSIVE state. OK : SENT_PASSIVE tested for in hub.c:hub_connect_user:!is_active -- is_active is true! OK : SENT_PASSIVE tested for in hub.c:hub_handle_command:$RevConnectToMe:!is_active -- cannot communicate? OK : KNOWN_ACTIVE set on SENT_PASSIVE in main.c:DC_MSG_VALIDATE_NICK:ok -- this is still true. === Coding notes: Error management: Assume ((x)v)asprintf only fails due to memory exhaustion (assuming format appears to be OK) or if it fails, just abort() Handle memory exhaustion errors by dying immediately Handle every other error that can occur! === User commands: $MyNick s MYNICK $Direction s i DIRECTION $Error s... * $FileLength i FILE_LENGTH $GetListLen - * $ListLen i * sent as a reply to $GetListLen $Get s$i GET $GetTestZBlock ? * $GetZBlock ? * $UGetZBlock ? * $UGetBlock ? * $Key s... KEY $Lock s... LOCK $Send - SEND $Sending [i] FILE_LENGTH $MaxedOut - FILE_LENGTH $Supports s... * Hub commands: $ValidateNick s $Key s... $Version s... $GetNickList - $MyPass s... $GetINFO s < s> s... $ConnectToMe s s:i $To: s From: s $s $Supports s... $RevConnectToMe s s $OpForceMove $Who:s$Where:s$Msg:s $Search ? $MyINFO ? $Quit ? $SR ? $HubName s... $Supports ? $UserCommand ? $Lock s Pk=s $Hello s $ForceMove ? $HubIsFull ? $ValidateDenide - $UserIP ? $NickList s{$$s} $OpList [s{$$s}] $GetPass - $BadPass - $LogedIn - $Kick ? $Capabilities [s{$s}] dchub:YES dc++:NO === curses client