mirror of
				https://github.com/anticensority/runet-censorship-bypass.git
				synced 2025-10-31 07:57:28 +03:00 
			
		
		
		
	Handle TUNN CONN FAILED, sanitize optioins page url params, fix #50
This commit is contained in:
		
							parent
							
								
									1eb005f085
								
							
						
					
					
						commit
						719d6eac9c
					
				
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -17,12 +17,15 @@ export default function getApp(theState) { | ||||||
|     constructor(props) { |     constructor(props) { | ||||||
| 
 | 
 | ||||||
|       super(props); |       super(props); | ||||||
|       const hash = window.location.hash.substr(1); | 
 | ||||||
|       const hashParams = new URLSearchParams(hash); |       const sanitizedUrl = theState.flags.ifOpenedUnsafely ? { hash: '', search: '' } : window.location; | ||||||
|  |       const hashParams = new URLSearchParams(sanitizedUrl.hash.substr(1)); | ||||||
|  |       const searchParams = new URLSearchParams(sanitizedUrl.search.substr(1)); | ||||||
|       this.state = { |       this.state = { | ||||||
|         status: 'Загрузка...', |         status: 'Загрузка...', | ||||||
|         ifInputsDisabled: false, |         ifInputsDisabled: false, | ||||||
|         hashParams: hashParams, |         hashParams, | ||||||
|  |         searchParams, | ||||||
|       }; |       }; | ||||||
| 
 | 
 | ||||||
|       this.setStatusTo = this.setStatusTo.bind(this); |       this.setStatusTo = this.setStatusTo.bind(this); | ||||||
|  | @ -62,9 +65,9 @@ export default function getApp(theState) { | ||||||
|       const uiComEtag = 'ui-last-comments-etag'; |       const uiComEtag = 'ui-last-comments-etag'; | ||||||
|       const uiLastNewsArr = 'ui-last-news-arr'; |       const uiLastNewsArr = 'ui-last-news-arr'; | ||||||
| 
 | 
 | ||||||
|       const statusFromHash = this.state.hashParams.get('status'); |       const statusFromUrl = this.state.searchParams.get('status'); | ||||||
|       if (statusFromHash) { |       if (statusFromUrl) { | ||||||
|         return this.setStatusTo(statusFromHash); |         return this.setStatusTo(statusFromUrl); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       const comDate = localStorage[uiComDate]; |       const comDate = localStorage[uiComDate]; | ||||||
|  |  | ||||||
|  | @ -17,24 +17,22 @@ export default function getFooter() { | ||||||
| 
 | 
 | ||||||
|   `;
 |   `;
 | ||||||
| 
 | 
 | ||||||
|   return function (props) { |   return (props) => ( | ||||||
|  |     <div class="horPadded"> | ||||||
|  |       <section class={scopedCss.statusRow}> | ||||||
|  |         <div class={scopedCss.status} style="will-change: contents"> | ||||||
|  |           {typeof(props.status) === 'string' ? <div dangerouslySetInnerHTML={{ __html: props.status }}></div> : props.status} | ||||||
|  |         </div> | ||||||
|  |       </section> | ||||||
| 
 | 
 | ||||||
|     return ( |       <footer class={scopedCss.controlRow + ' horFlex nowrap'}> | ||||||
|       <div class="horPadded"> |         <input type="button" value={chrome.i18n.getMessage('Finish')} disabled={props.ifInputsDisabled} onClick={() => window.close()} /> | ||||||
|         <section class={scopedCss.statusRow}> |         <a href="https://rebrand.ly/ac-donate">{chrome.i18n.getMessage('Donate')}</a> | ||||||
|           <div clss={scopedCss.status} style="will-change: contents">{props.status}</div> |         <a data-in-bg="false" href="../troubleshoot/index.html"> | ||||||
|         </section> |           {chrome.i18n.getMessage('ProblemsQ')} | ||||||
| 
 |         </a> | ||||||
|         <footer class={scopedCss.controlRow + ' horFlex nowrap'}> |       </footer> | ||||||
|           <input type="button" value={chrome.i18n.getMessage('Finish')} disabled={props.ifInputsDisabled} onClick={() => window.close()} /> |     </div> | ||||||
|           <a href="https://rebrand.ly/ac-donate">{chrome.i18n.getMessage('Donate')}</a> |   ); | ||||||
|           <a data-in-bg="false" href="../troubleshoot/index.html"> |  | ||||||
|             {chrome.i18n.getMessage('ProblemsQ')} |  | ||||||
|           </a> |  | ||||||
|         </footer> |  | ||||||
|       </div> |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -9,7 +9,13 @@ import getApp from './components/App'; | ||||||
| chrome.runtime.getBackgroundPage( (bgWindow) => | chrome.runtime.getBackgroundPage( (bgWindow) => | ||||||
|   bgWindow.apis.errorHandlers.installListenersOn( |   bgWindow.apis.errorHandlers.installListenersOn( | ||||||
|     window, 'PUP', async() => { |     window, 'PUP', async() => { | ||||||
| 
 |       /* | ||||||
|  |         `Extension context invalidated` error is thrown if `window.closed` is true and call to | ||||||
|  |         `window.chrome.i18n` or other `window.chrome` api happens. Use bgWindow.chrome instead. | ||||||
|  |         Use winChrome for tab-related calls like winChrome.tabs.getCurrent. | ||||||
|  |       */ | ||||||
|  |       window.winChrome = window.chrome; | ||||||
|  |       window.chrome = bgWindow.chrome; | ||||||
|       let theState; |       let theState; | ||||||
|       { |       { | ||||||
|         const apis = bgWindow.apis; |         const apis = bgWindow.apis; | ||||||
|  | @ -29,15 +35,23 @@ chrome.runtime.getBackgroundPage( (bgWindow) => | ||||||
|       // IF INSIDE OPTIONS TAB
 |       // IF INSIDE OPTIONS TAB
 | ||||||
| 
 | 
 | ||||||
|       const currentTab = await new Promise( |       const currentTab = await new Promise( | ||||||
|         (resolve) => chrome.tabs.query( |         (resolve) => winChrome.tabs.query( | ||||||
|           {active: true, currentWindow: true}, |           {active: true, currentWindow: true}, | ||||||
|           ([tab]) => resolve(tab) |           ([tab]) => resolve(tab), | ||||||
|         ) |         ) | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       theState.flags.ifInsideOptionsPage = !currentTab || currentTab.url.startsWith('chrome://extensions/?options=') || currentTab.url.startsWith('about:addons'); |       theState.flags.ifInsideOptionsPage = !currentTab || currentTab.url.startsWith('chrome://extensions/?options=') || currentTab.url.startsWith('about:addons'); | ||||||
|       theState.currentTab = currentTab; |       theState.currentTab = currentTab; | ||||||
| 
 | 
 | ||||||
|  |       // If opened not via popup and not via options modal.
 | ||||||
|  |       // E.g., if opened via copy-pasting an URL into the address bar from somewhere.
 | ||||||
|  |       // If browser is not Chrome (Opera, e.g.) then options page may be opened in a separate tab
 | ||||||
|  |       // and then you will get a false positive.
 | ||||||
|  |       theState.flags.ifOpenedUnsafely = Boolean(await new Promise( | ||||||
|  |         (resolve) => winChrome.tabs.getCurrent(resolve), | ||||||
|  |       )); | ||||||
|  | 
 | ||||||
|       // STATE DEFINED, COMPOSE.
 |       // STATE DEFINED, COMPOSE.
 | ||||||
| 
 | 
 | ||||||
|       appendGlobalCss(document, theState); |       appendGlobalCss(document, theState); | ||||||
|  |  | ||||||
|  | @ -2,7 +2,88 @@ | ||||||
| 
 | 
 | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|   chrome.webNavigation.onErrorOccurred.addListener((details) => { |   const proxySideErrors = [ | ||||||
|  |     'net::ERR_TUNNEL_CONNECTION_FAILED', | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|  |   const urlToA = (url) => new URL(url).host.link( | ||||||
|  |     encodeURIComponent(url), | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   const isProxyErrorHandledAsync = async (details) => { | ||||||
|  | 
 | ||||||
|  |     if (!proxySideErrors.includes(details.error) || details.type === 'main_frame') { | ||||||
|  |       // Main frame websocket errors are followed by webnavigation errors
 | ||||||
|  |       // which chrome-internals code resets the state of the popup.
 | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     let fromPageHref = ''; | ||||||
|  |     let fromPageHtml = ''; | ||||||
|  |     let youMayReportHtml = ''; | ||||||
|  |     const initiator = details.initiator !== 'null' && details.initiator; | ||||||
|  |     try { | ||||||
|  |       if (initiator) { | ||||||
|  |         fromPageHref = new URL(initiator).href; // Sanitize: only urls, not other stuff.
 | ||||||
|  |         fromPageHtml = ` со страницы ${urlToA(fromPageHref)}`; | ||||||
|  |       } | ||||||
|  |       youMayReportHtml = ` Вы можете <b>${'сообщить об ошибке'.link( | ||||||
|  |         encodeURIComponent( | ||||||
|  |           '/pages/report-proxy-error/index.html?' + | ||||||
|  |           new URLSearchParams({ | ||||||
|  |             fromPageHref, | ||||||
|  |             requestFailedTo: new URL(details.url).href, | ||||||
|  |           }), | ||||||
|  |         ), | ||||||
|  |       )}</b> администратору прокси.`; | ||||||
|  |     } catch(e) { | ||||||
|  |       /* Suppress for malformed urls. */ | ||||||
|  |       console.log('Error handling malformed URLs:', details); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const tabId = details.tabId; | ||||||
|  |     const popupPrefix = chrome.runtime.getURL(`/pages/options/index.html?status=<span style="color: red">🔥 Прокси-сервер отказался обслуживать запрос к `); | ||||||
|  |     const oldPopup = await new Promise((resolve) => chrome.browserAction.getPopup({ tabId }, resolve)); | ||||||
|  |     if (decodeURIComponent(oldPopup).startsWith(popupPrefix)) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     const popup = `${popupPrefix}${urlToA(details.url)}${fromPageHtml}</span>. Это могло быть намеренно или по ошибке.${youMayReportHtml}#tab=exceptions`; | ||||||
|  | 
 | ||||||
|  |     chrome.browserAction.setPopup({ | ||||||
|  |       tabId, | ||||||
|  |       popup, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     chrome.browserAction.setBadgeBackgroundColor({ | ||||||
|  |       tabId, | ||||||
|  |       color: 'red', | ||||||
|  |     }); | ||||||
|  |     chrome.browserAction.setBadgeText({ | ||||||
|  |       tabId, | ||||||
|  |       text: '❗', | ||||||
|  |     }); | ||||||
|  |     let limit = 5; | ||||||
|  |     let ifOnTurn = true; | ||||||
|  |     let ifError = false; | ||||||
|  |     const flip = () => { | ||||||
|  | 
 | ||||||
|  |       if (!ifOnTurn && !--limit || ifError) { | ||||||
|  |         clearInterval(timer); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       chrome.browserAction.setBadgeText({ | ||||||
|  |         tabId, | ||||||
|  |         text: ifOnTurn ? '❗' : '', | ||||||
|  |       }, () => { | ||||||
|  |         ifError = chrome.runtime.lastError; | ||||||
|  |       }); | ||||||
|  |       ifOnTurn = !ifOnTurn; | ||||||
|  |     }; | ||||||
|  |     flip(); | ||||||
|  |     const timer = setInterval(flip, 500); | ||||||
|  |     return true; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   chrome.webNavigation.onErrorOccurred.addListener(async (details) => { | ||||||
| 
 | 
 | ||||||
|     const tabId = details.tabId; |     const tabId = details.tabId; | ||||||
|     if ( !(details.frameId === 0 && tabId >= 0) || |     if ( !(details.frameId === 0 && tabId >= 0) || | ||||||
|  | @ -12,13 +93,16 @@ | ||||||
|           ].includes(details.error) ) { |           ].includes(details.error) ) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |     if (await isProxyErrorHandledAsync(details)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     chrome.browserAction.setPopup({ |     chrome.browserAction.setPopup({ | ||||||
|       tabId, |       tabId, | ||||||
|       popup: './pages/options/index.html#tab=exceptions&status=Правый клик по иконке — меню инструментов!', |       popup: './pages/options/index.html?status=Правый клик по иконке — меню инструментов!#tab=exceptions', | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     window.chrome.browserAction.setBadgeBackgroundColor({ |     chrome.browserAction.setBadgeBackgroundColor({ | ||||||
|       tabId, |       tabId, | ||||||
|       color: '#4285f4', |       color: '#4285f4', | ||||||
|     }); |     }); | ||||||
|  | @ -29,4 +113,9 @@ | ||||||
| 
 | 
 | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   chrome.webRequest.onErrorOccurred.addListener( | ||||||
|  |     isProxyErrorHandledAsync, | ||||||
|  |     {urls: ['<all_urls>']}, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ | ||||||
| 
 | 
 | ||||||
|   const setRedBadge = (opts) => { |   const setRedBadge = (opts) => { | ||||||
| 
 | 
 | ||||||
|     window.chrome.browserAction.setBadgeBackgroundColor({ |     chrome.browserAction.setBadgeBackgroundColor({ | ||||||
|       color: '#db4b2f', |       color: '#db4b2f', | ||||||
|     }); |     }); | ||||||
|     chrome.browserAction.setBadgeText(opts); |     chrome.browserAction.setBadgeText(opts); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="utf-8"> | ||||||
|  |     <title>Сообщить об ошибке прокси-сервера</title> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  |     <style> | ||||||
|  |       :root { | ||||||
|  |         font-size: 1.3em; | ||||||
|  |       } | ||||||
|  |     </style> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  | 
 | ||||||
|  |     <h1>Сообщить об ошибке прокси-сервера</h1> | ||||||
|  |       Перешлите администратору вашего прокси следующее: | ||||||
|  |       <fieldset> | ||||||
|  |         <pre id="errorInfo"></pre> | ||||||
|  |       </fieldset> | ||||||
|  |       Вот известные нам электронные адреса для популярных прокси-серверов (кликните по email для открытия шаблона письма): | ||||||
|  |       <ol> | ||||||
|  |         <li> | ||||||
|  |           Только если вы используете <a | ||||||
|  |             href="https://antizapret.prostovpn.org">Антизапрет</a>: | ||||||
|  |           <a | ||||||
|  |             href="mailto:antizapret@prostovpn.org">antizapret@prostovpn.org</a>. | ||||||
|  |         </li> | ||||||
|  |       </ol> | ||||||
|  |     <script src="./index.js"></script> | ||||||
|  |     <script src="../lib/keep-links-clickable.js"></script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | chrome.runtime.getBackgroundPage( (bgWindow) => | ||||||
|  |   bgWindow.apis.errorHandlers.installListenersOn( | ||||||
|  |     window, 'PRERR', () => { | ||||||
|  | 
 | ||||||
|  |       const params = new URLSearchParams(location.search.substr(1)); | ||||||
|  |       const requestFailedTo = params.get('requestFailedTo'); | ||||||
|  |       const fromPageHref = params.get('fromPageHref') || requestFailedTo; | ||||||
|  | 
 | ||||||
|  |       const acr = bgWindow.apis.antiCensorRu; | ||||||
|  |       const pacKey = acr.getCurrentPacProviderKey(); | ||||||
|  |       const pacModTime = acr.getLastModifiedForKey(pacKey); | ||||||
|  | 
 | ||||||
|  |       const errorReport = ` | ||||||
|  | Your proxy blocked the following request: | ||||||
|  |   * Request was from page: ${fromPageHref} | ||||||
|  |   * To address: ${requestFailedTo} | ||||||
|  |   * Used PAC-script: ${pacKey} | ||||||
|  |   * Its Last-Modified HTTP-header: ${pacModTime} | ||||||
|  | I think it's a mistake! Could you, please, take action to fix it. | ||||||
|  | Thank you! | ||||||
|  | 
 | ||||||
|  | Ваш прокси-сервер заблокировал следующий запрос: | ||||||
|  |   * Запрос был со страницы: ${fromPageHref} | ||||||
|  |   * Адрес запроса: ${requestFailedTo} | ||||||
|  |   * Мой PAC-скрипт: ${pacKey} | ||||||
|  |   * Его HTTP-заголовок Last-Modified: ${pacModTime} | ||||||
|  | Я думаю, это произошло по ошибке! Пожалуйста, примите действия для её исправления. | ||||||
|  | Спасибо! | ||||||
|  |       `.trim();
 | ||||||
|  |       errorInfo.innerText = errorReport; | ||||||
|  |       document.querySelectorAll('a[href^="mailto:"]').forEach((a) => { | ||||||
|  | 
 | ||||||
|  |         a.href = `${a.href}?subject=${encodeURIComponent(new URL(requestFailedTo).hostname)} TUNNEL_CONNECTION_FAILED&body=${encodeURIComponent(errorReport)}`; | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |   ), | ||||||
|  | ); | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user