Android VpnService കണക്ട് ചെയ്യുന്നു പക്ഷേ പാക്കറ്റുകൾ നെറ്റ്വർക്കിൽ എത്തുന്നില്ല
ടണൽ ഓണാണ്, പേജുകൾ ഇപ്പോഴും പരാജയപ്പെടുന്നു: VPN സർവറിനെ കുറ്റപ്പെടുത്തുന്നതിന് മുൻപ് വായനാ മോഡ്, സോക്കറ്റ് സംരക്ഷണം, TCP പിന്തുണ, റൂട്ട് ലിസ്റ്റ് എന്നിവ പരിശോധിക്കുക.
ടണൽ ഓണാകുന്നു, കീ ഐക്കൺ വരുന്നു, എന്നാൽ ബ്രൗസറിലെ എല്ലാ അഭ്യർത്ഥനകളും സമയം കഴിഞ്ഞ് പരാജയപ്പെടുന്നു. VpnService ആപ്പിൽ ഇത് സാധാരണയായി വായനാ ലൂപ്പ് ജീവനോടെയുണ്ട്, ഔട്ട്പുട്ട് പകുതി തകർന്നിരിക്കുന്നു എന്നാണ് അർത്ഥം, സർവർ ഹാൻഡ്ഷേക്ക് നിരസിച്ചു എന്നല്ല. Android ഇന്റർഫേസ് ഉണ്ടാക്കി, ആപ്പിന്റെ ട്രാഫിക് അതിലേക്ക് മാറ്റി, നിങ്ങളുടെ പ്രോസസിന് ഫയൽ ഡിസ്ക്രിപ്റ്റർ നൽകി. ആ ഡിസ്ക്രിപ്റ്ററിന് ശേഷമുള്ളതെല്ലാം നിങ്ങളുടെ കോഡാണ്, ഒരു വിളി വിട്ടുപോയാൽ മുഴുവൻ സ്ട്രീമും വിഴുങ്ങപ്പെടും.
സിസ്റ്റം എന്ത് നൽകുന്നു, എന്ത് നൽകുന്നില്ല
establish() tun ഇന്റർഫേസിനായി ParcelFileDescriptor നൽകുന്നു. പുറത്തേക്കുള്ള IP പാക്കറ്റുകൾ എഴുതുന്നത് സിസ്റ്റമാണ്; നിങ്ങളുടെ പ്രോസസ് അവ വായിച്ച്, എങ്ങോട്ട് പോകണമെന്ന് തീരുമാനിച്ച്, സാധാരണ സോക്കറ്റിലൂടെ അയക്കണം. താഴെ കെർണൽ ഫോർവേഡിംഗ് ഇല്ല, അതിനാൽ ആരും വായിക്കുന്നില്ലെങ്കിൽ പാക്കറ്റുകൾ ഉപേക്ഷിക്കപ്പെടും, ഇന്റർഫേസ് കൗണ്ടറുകൾ ഒരിക്കലും നീങ്ങില്ല.
ഊഹിക്കുന്നതിന് പകരം എണ്ണൽ തുടങ്ങുക. വിജയകരമായ ഓരോ വായനയിലും കൗണ്ടർ കൂട്ടി, ആദ്യ പാക്കറ്റുകളുടെ ലക്ഷ്യവും പ്രോട്ടോക്കോളും രേഖപ്പെടുത്തുക. ട്രാഫിക് ഉണ്ടാക്കുമ്പോൾ കൗണ്ടർ പൂജ്യത്തിൽ നിൽക്കുന്നുവെങ്കിൽ, തകരാർ ഫോർവേഡിംഗ് കോഡിന് മുകളിലാണ്; അന്വേഷണം builder ക്രമീകരണത്തിലും വായനാ മോഡിലും ചുരുങ്ങും. കൗണ്ടർ നീങ്ങിയിട്ടും ബ്രൗസർ പരാജയപ്പെടുന്നുവെങ്കിൽ, തകരാർ അയക്കുന്നതിലോ തിരികെ വരുന്നതിലോ ഉള്ള പാതയിലാണ്.
ബ്ലോക്കിംഗ് വായന ലൂപ്പ് നീങ്ങുന്നുണ്ടോ എന്ന് തീരുമാനിക്കുന്നു
setBlocking(true) ഡിസ്ക്രിപ്റ്ററിനെ ബ്ലോക്കിംഗ് മോഡിലാക്കുന്നു, API 21 മുതൽ ഇതുണ്ട്. പഴയ പതിപ്പുകളിൽ പരിഹാരം ചെറിയ JNI സഹായിയാണ്; fcntl ഉപയോഗിച്ച് അസംസ്കൃത ഡിസ്ക്രിപ്റ്ററിൽ നിന്ന് O_NONBLOCK മായ്ക്കുന്നു. മോഡ് പ്രധാനമാണ്, കാരണം കാത്തിരിക്കുന്ന പാക്കറ്റില്ലാത്ത വായനയ്ക്ക് എന്ത് ചെയ്യാമെന്ന് അത് തീരുമാനിക്കുന്നു.
നോൺ-ബ്ലോക്കിംഗ് മോഡിൽ ആ വായന ഉടൻ നെഗറ്റീവ് മൂല്യവുമായി മടങ്ങും; ഫലം അവഗണിക്കുന്ന ലൂപ്പ് ഒരു ബൈറ്റ് ട്രാഫിക്കും നീക്കാതെ കോർ കറക്കും. ബ്ലോക്കിംഗ് മോഡിൽ വായന കാത്തിരിക്കും, ഫോർവേഡിംഗ് ത്രെഡിന് അതാണ് വേണ്ടത്, പക്ഷേ പിന്നെ ത്രെഡ് തടയാൻ കഴിയില്ല. വായന പരാജയപ്പെടുത്താനുള്ള പിന്തുണയുള്ള വഴി ഡിസ്ക്രിപ്റ്റർ അടയ്ക്കുകയാണ്, അപ്പോൾ ലൂപ്പിന് പുറത്തുകടക്കാം. പ്രോസസ് കൊല്ലുകയോ ത്രെഡ് തടയുകയോ ചെയ്താൽ അടുത്ത തുടക്കം ഇന്റർഫേസുമായി ഇനി ചേരാത്ത ഡിസ്ക്രിപ്റ്ററുമായി ഇരിക്കാം.
ഓരോ വിളിയിലും ഇന്റർഫേസിന്റെ MTU വലിപ്പമുള്ള ബഫറിലേക്ക് ഒരു പാക്കറ്റ് വായിക്കുക; പൂജ്യമോ നെഗറ്റീവ് മടക്കമോ ശൂന്യ പാക്കറ്റല്ല, പിശകാണ്.
ആദ്യ അയക്കലിന് മുൻപ് protect(), പിൻപല്ല
എൻകാപ്സുലേറ്റ് ചെയ്ത പാക്കറ്റ് വഹിക്കുന്ന എല്ലാ സോക്കറ്റും ആദ്യം സംരക്ഷിക്കണം. സംരക്ഷിക്കാത്ത സോക്കറ്റ് നിങ്ങൾ സ്ഥാപിച്ച അതേ റൂട്ടിംഗ് നിയമങ്ങൾ പാലിക്കും, അതിനാൽ അതിന്റെ പാക്കറ്റുകൾ ടണലിലേക്ക് തിരികെ പോകും, നിങ്ങളുടെ വായനാ ലൂപ്പ് അവ വീണ്ടും എടുക്കും. പിന്നെ ടണൽ സ്വന്തം ഔട്ട്പുട്ട് അനന്തമായി ഫോർവേഡ് ചെയ്യും; പുറത്തുനിന്ന് അത് തുറന്ന് ഒരിക്കലും ഉത്തരം നൽകാത്ത കണക്ഷൻ പോലെ തോന്നും.
ക്രമം protect, പിന്നെ connect അല്ലെങ്കിൽ bind, പിന്നെ send. connect-ന് ശേഷം protect വിളിച്ചാൽ മുൻ റൂട്ട് തീരുമാനം നിലനിൽക്കാം, അതിനാൽ വിളി true തന്നാലും സോക്കറ്റ് തെറ്റായ വഴി തുടരും. മടക്കുന്ന ബൂളിയൻ പരിശോധിച്ച്, false ആകുമ്പോൾ പ്രവർത്തിക്കാനാവാത്ത send-ലേക്ക് കയറുന്നതിന് പകരം ഉച്ചത്തിൽ പരാജയപ്പെടുക.
protect() ഒരിക്കലും തൊടാത്ത സോക്കറ്റിൽ sendto നൽകുന്ന അനുമതി പിശക് ഈ തെറ്റിന്റെ സാധാരണ അടയാളമാണ്.
UDP മാതൃക HTTPS കൊണ്ടുപോകുന്നില്ല
മിക്ക ട്യൂട്ടോറിയലുകളും പകർത്തുന്ന ഫോർവേഡിംഗ് മാതൃക UDP മാത്രം കൈകാര്യം ചെയ്യുന്നു. TCP-യ്ക്ക് ഹാൻഡ്ഷേക്ക്, ക്രമ നമ്പറുകൾ, സ്വീകരണങ്ങൾ, വീണ്ടും അയക്കൽ എന്നിവ വേണം; tun ഡിസ്ക്രിപ്റ്ററും സോക്കറ്റും തമ്മിൽ പേലോഡ് പകർത്തുന്ന ലൂപ്പ് അവയൊന്നും നൽകുന്നില്ല. അതുകൊണ്ടാണ് UDP DNS ചോദ്യം കടന്നുപോകുമ്പോൾ എല്ലാ HTTPS അഭ്യർത്ഥനയും നിൽക്കുന്നത്. ഉപയോക്തൃ സ്ഥലത്ത് TCP സ്റ്റാക്ക് പ്രവർത്തിപ്പിക്കുക, അല്ലെങ്കിൽ ഉപകരണത്തിൽ TCP അവസാനിപ്പിച്ച് സ്ട്രീം ഫോർവേഡ് ചെയ്യുന്ന പ്രാദേശിക പ്രോക്സിക്ക് കണക്ഷനുകൾ നൽകുക.
എന്തും വീണ്ടും എഴുതുന്നതിന് മുൻപ് വിഭജനം പരിശോധിക്കുക: ഒരു UDP ചോദ്യം അയച്ച് അതേ ഹോസ്റ്റിലേക്ക് ഒരു TCP കണക്ഷൻ തുറക്കുക. UDP പാത പ്രവർത്തിച്ച് TCP പാത ആദ്യ SYN-ൽ നിന്നാൽ, പ്രശ്നം ഫോർവേഡിംഗ് കോഡിലാണ്, സർവറിലല്ല. ആ ഒരു താരതമ്യം ഉപയോഗപ്രദമല്ലാത്ത സർവർ ലോഗുകൾ വായിക്കുന്നതിൽ നഷ്ടപ്പെടുന്ന സമയം ലാഭിക്കുന്നു.
റൂട്ട് ലിസ്റ്റും ആപ്പ് അനുസരിച്ചുള്ള ഫിൽട്ടറുകളും കാണുന്നത് പരിമിതപ്പെടുത്തുന്നു
addAddress, addRoute, addDns എന്നിവ സിസ്റ്റം ടണലിലേക്ക് എന്ത് അയക്കുമെന്ന് നിർണ്ണയിക്കുന്നു. റൂട്ട് ലിസ്റ്റിന് പുറത്തുള്ള ലക്ഷ്യം ഒരിക്കലും നിങ്ങളുടെ ഡിസ്ക്രിപ്റ്ററിൽ എത്തില്ല, അതിനാൽ എത്ര ഫോർവേഡിംഗ് ലോജിക് ചേർത്താലും അത് ശരിയാക്കില്ല. നിങ്ങൾ നടത്തുന്ന builder വിളികൾ രേഖപ്പെടുത്തി, ആപ്പ് യഥാർത്ഥത്തിൽ എത്താൻ ശ്രമിക്കുന്ന വിലാസങ്ങളുമായി താരതമ്യം ചെയ്യുക.
ആപ്പ് അനുസരിച്ചുള്ള തിരഞ്ഞെടുപ്പിന് സ്വന്തം പരിധിയുണ്ട്: addAllowedApplication, addDisallowedApplication എന്നിവയ്ക്ക് API 21 വേണം, പഴയ പതിപ്പുകളിൽ ആ രീതികൾ ഇല്ല. പാക്കറ്റ് അനുസരിച്ച് ഫിൽട്ടർ ചെയ്യുന്ന രൂപകൽപ്പന അവിടെ പ്രവർത്തിക്കില്ല.
പുനഃസ്ഥാപനം പഴയ ഡിസ്ക്രിപ്റ്ററുകൾ ബാക്കിയാക്കുന്നു
establish() വീണ്ടും വിളിച്ചാൽ പുതിയ ഡിസ്ക്രിപ്റ്റർ വരും, മുൻ ഇന്റർഫേസ് അടയും. പഴയ ഡിസ്ക്രിപ്റ്റർ ഇനി അസാധുവാണ്, അതിൽ ഇപ്പോഴും ബ്ലോക്ക് ചെയ്തിരിക്കുന്ന ത്രെഡിന് പിശക് ലഭിക്കും; പഴയ പാതയ്ക്ക് ഉണ്ടാക്കിയ സോക്കറ്റുകൾ മാറ്റേണ്ടിവന്നേക്കാം. onRevoke ഉപയോക്താവോ സിസ്റ്റമോ എടുത്തുകളഞ്ഞ ടണലിനുള്ള കോൾബാക്കാണ്. പുനഃസ്ഥാപനത്തിന് മുൻപ് വായനക്കാരനെ നിർത്തി ജീവിതചക്രം ഒരു ഉടമയിൽ നിർത്തിയാൽ ബന്ധിപ്പിച്ചെങ്കിലും മരിച്ച അവസ്ഥകൾ ഒഴിവാക്കാം.
ഓരോ പിശകും അതിന്റെ ഘട്ടം അനുസരിച്ച് വായിക്കുക. recvfrom-ൽ നിന്നുള്ള കണക്ഷൻ റീസെറ്റ് എന്നാൽ നിങ്ങളുടെ കോഡ് ഇപ്പോഴും വായിക്കുമ്പോൾ ദൂരെയുള്ള അറ്റം അടച്ചു എന്നാണ്. മോശം വിലാസത്തിന്റെ പിശക് ഹെഡർ പാഴ്സിംഗിലേക്കോ ഓഫ്സെറ്റ് ബഗ്ഗിലേക്കോ ആണ്, നെറ്റ്വർക്കിലേക്കല്ല.
ജോലിക്രമം: ബ്ലോക്കിംഗ് മോഡ് ഓൺ, ഓരോ വായനയ്ക്കും ഒരു പാക്കറ്റ്, ഹെഡർ പാഴ്സ് ചെയ്തു, സോക്കറ്റ് സംരക്ഷിച്ചു, പേലോഡ് അയച്ചു, കൈമാറ്റത്തിൽ വൃത്തിയായി അടച്ചു, ലക്ഷ്യം ഉൾക്കൊള്ളുന്ന റൂട്ട് ലിസ്റ്റ്.