tor-commits
Threads by month
- ----- 2025 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
October 2013
- 17 participants
- 1285 discussions
[translation/liveusb-creator_completed] Update translations for liveusb-creator_completed
by translation@torproject.org 28 Oct '13
by translation@torproject.org 28 Oct '13
28 Oct '13
commit 8efe44f1100a4da988bfd04033abd0d01d727107
Author: Translation commit bot <translation(a)torproject.org>
Date: Mon Oct 28 16:45:58 2013 +0000
Update translations for liveusb-creator_completed
---
tr/tr.po | 569 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 569 insertions(+)
diff --git a/tr/tr.po b/tr/tr.po
new file mode 100644
index 0000000..678df7e
--- /dev/null
+++ b/tr/tr.po
@@ -0,0 +1,569 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Bloodia <aycaom(a)hotmail.com>, 2013
+# muaz742, 2013
+# volkangezer <volkangezer(a)gmail.com>, 2013
+# yozel <iletisim(a)yasinozel.com.tr>, 2013
+msgid ""
+msgstr ""
+"Project-Id-Version: The Tor Project\n"
+"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
+"POT-Creation-Date: 2013-09-09 11:21+0200\n"
+"PO-Revision-Date: 2013-10-28 16:40+0000\n"
+"Last-Translator: volkangezer <volkangezer(a)gmail.com>\n"
+"Language-Team: Turkish (http://www.transifex.com/projects/p/torproject/language/tr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: tr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: ../liveusb/dialog.py:150 ../liveusb/launcher_ui.py:149
+#, python-format
+msgid "%(distribution)s LiveUSB Creator"
+msgstr "%(distribution)s ÇalışırUSB Oluşturucu"
+
+#: ../liveusb/gui.py:776
+#, python-format
+msgid "%(filename)s selected"
+msgstr "%(filename)s seçildi"
+
+#: ../liveusb/creator.py:1004
+#, python-format
+msgid "%s already bootable"
+msgstr "%s zaten başlatılabilir"
+
+#: ../liveusb/launcher_ui.py:156
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
+"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Need help? Read the </span><a href=\"file:///usr/share/doc/tails/website/doc/first_steps/usb_installation.en.html\"><span style=\" text-decoration: underline; color:#0000ff;\">documentation</span></a><span style=\" font-size:10pt;\">.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Yardıma mı ihtiyacın var? </span><a href=\"file:///usr/share/doc/tails/website/doc/first_steps/usb_installation.en.html\"><span style=\" text-decoration: underline; color:#0000ff;\">Dçkümanları</span></a><span style=\" font-size:10pt;\"> okuyabilirsin.</span></p></body></html>"
+
+#: ../liveusb/launcher_ui.py:151
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
+"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Copy the running Tails onto a USB stick. All data on the target drive will be lost.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Çalışan Tails'i bir USB belleğe kopyala. Hedef sürücüdeki tüm veri silinecek.</span></p></body></html>"
+
+#: ../liveusb/launcher_ui.py:153
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
+"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Copy the running Tails onto an already installed Tails USB stick. Other partitions found on the stick are preserved.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Çalışan Tails'i daha önceden kurulmuş Tails USB belleğine kopyala. Bellekte bulunan diğer birimler korunacaktır.</span></p></body></html>"
+
+#: ../liveusb/launcher_ui.py:155
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
+"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Upgrade an already installed Tails USB stick from a new ISO image.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Daha önceden kurulmuş Tails USB belleğini, bir ISO kalıbından yükselt.</span></p></body></html>"
+
+#: ../liveusb/dialog.py:154
+msgid "Alt+B"
+msgstr "Alt+B"
+
+#: ../liveusb/dialog.py:153
+msgid "Browse"
+msgstr "Göz At"
+
+#: ../liveusb/dialog.py:160
+msgid ""
+"By allocating extra space on your USB stick for a persistent overlay, you "
+"will be able to store data and make permanent modifications to your live "
+"operating system. Without it, you will not be able to save data that will "
+"persist after a reboot."
+msgstr "USB belleğinizde kalıcı kaplama için ek alan ayırarak, çalışır işletim sisteminizde kalıcı değişiklikler yapmanızı ve veri depolamanızı mümkün kılarsınız. Bu olmadan, yeniden başlatma işleminden sonra kaydedilen veriler saklanamaz."
+
+#: ../liveusb/creator.py:1106 ../liveusb/creator.py:1369
+#, python-format
+msgid "Calculating the SHA1 of %s"
+msgstr "%s'in SHA1'ı hesaplanıyor"
+
+#: ../liveusb/creator.py:1317
+msgid "Cannot find"
+msgstr "Bulunamadı"
+
+#: ../liveusb/creator.py:545
+#, python-format
+msgid "Cannot find device %s"
+msgstr "Cihaz bulunamadı %s"
+
+#: ../liveusb/launcher_ui.py:150
+msgid ""
+"Clone\n"
+"&&\n"
+"Install"
+msgstr "Klonla\n&&\nKur"
+
+#: ../liveusb/launcher_ui.py:152
+msgid ""
+"Clone\n"
+"&&\n"
+"Upgrade"
+msgstr "Klonla\n&&\nGüncelle"
+
+#: ../liveusb/dialog.py:165
+msgid "Create Live USB"
+msgstr "Live USB oluştur"
+
+#: ../liveusb/creator.py:400
+#, python-format
+msgid "Creating %sMB persistent overlay"
+msgstr "%sMB kalıcı kaplaması oluşturuluyor"
+
+#: ../liveusb/gui.py:556
+msgid ""
+"Device is not yet mounted, so we cannot determine the amount of free space."
+msgstr "Cihaz henüz takılı değil, bu yüzden boş alan hesaplanması şu an mümkün değil."
+
+#: ../liveusb/dialog.py:157
+#, python-format
+msgid "Download %(distribution)s"
+msgstr "İndir %(distribution)s"
+
+#: ../liveusb/gui.py:750
+msgid "Download complete!"
+msgstr "İndirme tamamlandı!s"
+
+#: ../liveusb/gui.py:754
+msgid "Download failed: "
+msgstr "İndirme başarısız:"
+
+#: ../liveusb/gui.py:88
+#, python-format
+msgid "Downloading %s..."
+msgstr "%s indiriliyor.."
+
+#: ../liveusb/creator.py:1102
+msgid "Drive is a loopback, skipping MBR reset"
+msgstr "Sürücü bir yansıma, MBR sıfırlama atlanıyor"
+
+#: ../liveusb/creator.py:808
+#, python-format
+msgid "Entering unmount_device for '%(device)s'"
+msgstr "'%(device)s' için unmount_device giriyor"
+
+#: ../liveusb/creator.py:1182
+msgid "Error probing device"
+msgstr "Sürücü araştırma hatası"
+
+#: ../liveusb/gui.py:227
+msgid ""
+"Error: Cannot set the label or obtain the UUID of your device. Unable to "
+"continue."
+msgstr "Hata: Etiket ayarlanamıyor veya aygıtınızın UUID'si alınamıyor. Devam edilemez."
+
+#: ../liveusb/creator.py:376
+msgid ""
+"Error: The SHA1 of your Live CD is invalid. You can run this program with "
+"the --noverify argument to bypass this verification check."
+msgstr "Hata: Live CD'nizin SHA1'i geçersizdir. Bu programı --noverify argümanıyla çalıştırarak bu onaylama adımını atlayabilirsiniz."
+
+#: ../liveusb/creator.py:145
+msgid "Extracting live image to USB device..."
+msgstr "Live .iso imajı USB cihazına çıkarılıyor..."
+
+#: ../liveusb/creator.py:1047
+#, python-format
+msgid "Formatting %(device)s as FAT32"
+msgstr "%(device)s FAT32 olarak formatlanıyor"
+
+#: ../liveusb/creator.py:140
+msgid "ISO MD5 checksum passed"
+msgstr "ISO MD5 checksum başarılı"
+
+#: ../liveusb/creator.py:138
+msgid "ISO MD5 checksum verification failed"
+msgstr "ISO MD5 checksum onaylanması başarısız"
+
+#: ../liveusb/dialog.py:156
+msgid ""
+"If you do not select an existing Live ISO, the selected release will be "
+"downloaded for you."
+msgstr "Eğer mevcut bir Live ISO seçmezseniz, seçili olan sürüm indirilecektir."
+
+#: ../liveusb/gui.py:619
+msgid "Installation complete!"
+msgstr "Kurulum tamamlandı!"
+
+#: ../liveusb/gui.py:279
+#, python-format
+msgid "Installation complete! (%s)"
+msgstr "Kurulum tamamlandı! (%s)"
+
+#: ../liveusb/gui.py:620
+msgid "Installation was completed. Press OK to close this program."
+msgstr "Kutulum tamamlandı. Lütfen programı kapatmak için TAMAM butonuna basın."
+
+#: ../liveusb/creator.py:900 ../liveusb/creator.py:1223
+msgid "Installing bootloader..."
+msgstr "Bootloader yükleniyor..."
+
+#: ../liveusb/gui.py:284
+msgid "LiveUSB creation failed!"
+msgstr "LiveUSB oluşturma başarısız!"
+
+#: ../liveusb/creator.py:1318
+msgid ""
+"Make sure to extract the entire liveusb-creator zip file before running this"
+" program."
+msgstr "Bu programı çalıştırmadan önce liveusb-creator zip dosyasının tamamını çıkarttığınızdan emin olun."
+
+#: ../liveusb/creator.py:1194
+msgid ""
+"Make sure your USB key is plugged in and formatted with the FAT filesystem"
+msgstr "USB belleğinizin takılı ve FAT dosya sistemi ile biçimlendirildiğinden emin olun"
+
+#: ../liveusb/creator.py:830
+#, python-format
+msgid "Mount %s exists after unmounting"
+msgstr "%s bağlantısı ayırma işleminden sonra mevcut"
+
+#: ../liveusb/gui.py:681 ../liveusb/gui.py:698
+msgid "Next"
+msgstr "İleri"
+
+#: ../liveusb/gui.py:562
+#, python-format
+msgid "No free space on device %(device)s"
+msgstr "%(device)s cihazında boş alan yok"
+
+#: ../liveusb/creator.py:797
+msgid "No mount points found"
+msgstr "Hiçbir bağlama noktası bulunamadı"
+
+#: ../liveusb/creator.py:393
+#, python-format
+msgid ""
+"Not enough free space on device.\n"
+"%dMB ISO + %dMB overlay > %dMB free space"
+msgstr "Yeterli boş alan yok.\n%dMB ISO + %dMB overlay > %dMB free space"
+
+#: ../liveusb/gui.py:543
+msgid "Partition is FAT16; Restricting overlay size to 2G"
+msgstr "Partisyon FAT16 formatında; Overlay boyutu 2G ile sınırlandırılıyor"
+
+#: ../liveusb/gui.py:539
+msgid "Partition is FAT32; Restricting overlay size to 4G"
+msgstr "Partisyon FAT32 formatında; Overlay boyutu 4G'a sınırlanıyor"
+
+#: ../liveusb/creator.py:226 ../liveusb/creator.py:837
+#, python-format
+msgid "Partitioning device %(device)s"
+msgstr "Cihaz partisyonlanıyor %(device)s"
+
+#: ../liveusb/gui.py:610
+msgid "Persistent Storage"
+msgstr "Kalıcı Depo"
+
+#: ../liveusb/dialog.py:161
+msgid "Persistent Storage (0 MB)"
+msgstr "Kalici Depo (0 MB)"
+
+#: ../liveusb/gui.py:679 ../liveusb/gui.py:696
+msgid "Press 'Next' if you wish to continue."
+msgstr "Devam etmek istiyorsanız 'İleri'ye basın."
+
+#: ../liveusb/gui.py:456
+msgid "Refreshing releases..."
+msgstr "Sürümler yenileniyor..."
+
+#: ../liveusb/gui.py:461
+msgid "Releases updated!"
+msgstr "Sürümler güncelleştirildi!"
+
+#: ../liveusb/creator.py:923 ../liveusb/creator.py:1241
+#, python-format
+msgid "Removing %(file)s"
+msgstr " %(file)s siliniyor"
+
+#: ../liveusb/creator.py:469
+msgid "Removing existing Live OS"
+msgstr "Mevcut Live OS kaldırılıyor"
+
+#: ../liveusb/creator.py:1096
+#, python-format
+msgid "Resetting Master Boot Record of %s"
+msgstr "%s'in Master Boot Record'ı sıfırlanıyor"
+
+#: ../liveusb/gui.py:761
+msgid "Select Live ISO"
+msgstr "Live ISO seçin"
+
+#: ../liveusb/creator.py:182
+msgid "Setting up OLPC boot file..."
+msgstr "OLPC önyükleme dosyası ayarlanıyor.."
+
+#: ../liveusb/creator.py:711
+#, python-format
+msgid ""
+"Some partitions of USB device %(device)s are mounted. They will be unmounted"
+" before starting the installation process."
+msgstr "%(device)s USB aygıtının bazı bölümleri bağlanmış. Kurulum işleminden önce bağlantıları kesilecek."
+
+#: ../liveusb/creator.py:131
+msgid ""
+"Source type does not support verification of ISO MD5 checksum, skipping"
+msgstr "Kaynak ISO MD5 checksum'ını onaylamasını desteklemiyor, atlanıyor."
+
+#: ../liveusb/creator.py:1130
+msgid "Synchronizing data on disk..."
+msgstr "Diskteki veriler senkronize ediliyor..."
+
+#: ../liveusb/dialog.py:159
+msgid "Target Device"
+msgstr "Hedef Cihaz"
+
+#: ../liveusb/gui.py:643
+msgid ""
+"The Master Boot Record on your device is blank. Pressing 'Create Live USB' "
+"again will reset the MBR on this device."
+msgstr "Cihazınızın Master Boot Record'u boş. \"Live USB oluştur\"a yeniden tıklamak, bu cihazın MBR'sini sıfırlayacaktır."
+
+#: ../liveusb/gui.py:764
+msgid ""
+"The selected file is unreadable. Please fix its permissions or select "
+"another file."
+msgstr "Seçilen dosya okunamıyor. Lütfen izinlerini kontrol edin veya başka bir dosya seçin."
+
+#: ../liveusb/creator.py:337
+#, python-format
+msgid ""
+"There was a problem executing the following command: `%(command)s`.\n"
+"A more detailed error log has been written to '%(filename)s'."
+msgstr "Aşağıdaki komutun yürütülmesinde bir problem çıktı:\n`%(command)s`.⏎\nDaha detaylı bir hata raporu '%(filename)s' adlı dosyaya yazıldı: "
+
+#: ../liveusb/dialog.py:151
+msgid ""
+"This button allows you to browse for an existing Live system ISO that you "
+"have previously downloaded. If you do not select one, a release will be "
+"downloaded for you automatically."
+msgstr "Bu tuş sizin daha önce yüklediğiniz mevcut Live sistem ISO'larınıza göz atmanıza olanak verir. Eğer hiç bir şey seçili değilse, bir sürüm otomatik olarak indirilmeye başlayacaktır."
+
+#: ../liveusb/dialog.py:164
+msgid ""
+"This button will begin the LiveUSB creation process. This entails "
+"optionally downloading a release (if an existing one wasn't selected), "
+"extracting the ISO to the USB device, creating the persistent overlay, and "
+"installing the bootloader."
+msgstr "Bu tuş Live USB yaratma sürecini başlatacaktır. Bu süreç, bir sürümün indirilmesi (eğer mevcut olan bir versiyon seçilmediyse), ISO imajının USB cihazına çıkarılması, kalıcı overlay'in oluşturulması ve bootloader'in yüklenmesini içerir."
+
+#: ../liveusb/dialog.py:158
+msgid ""
+"This is the USB stick that you want to install your Live system on. This "
+"device must be formatted with the FAT filesystem."
+msgstr "Bu Live sisteminizi yüklemek istediğiniz USB cihazı. Bu cihaz FAT dosya sistemi ile formatlanmalıdır. "
+
+#: ../liveusb/dialog.py:163
+msgid ""
+"This is the progress bar that will indicate how far along in the LiveUSB "
+"creation process you are"
+msgstr "Bu ilerleme çubuğu, ÇalışırUSB oluşturma sürecinin hangi adımınızda olduğunu belirtecek"
+
+#: ../liveusb/dialog.py:162
+msgid "This is the status console, where all messages get written to."
+msgstr "Bu, tüm iletilerin yazılacağı durum konsoludur"
+
+#: ../liveusb/creator.py:895
+#, python-format
+msgid "Unable to change volume label: %(message)s"
+msgstr "Birim etiketi değiştirme başarısız: %(message)s"
+
+#: ../liveusb/creator.py:478 ../liveusb/creator.py:489
+#, python-format
+msgid "Unable to chmod %(file)s: %(message)s"
+msgstr "chmod başarısız %(file)s: %(message)s"
+
+#: ../liveusb/creator.py:459
+#, python-format
+msgid "Unable to copy %(infile)s to %(outfile)s: %(message)s"
+msgstr "%(infile)s %(outfile)s'a kopyalanamadı: %(message)s"
+
+#: ../liveusb/gui.py:418
+msgid "Unable to find any USB drive"
+msgstr "Herhangi bir USB sürücü bulunamadı"
+
+#: ../liveusb/creator.py:1184
+msgid "Unable to find any removable device"
+msgstr "Kaldırılabilir bir cihaz bulunamadı"
+
+#: ../liveusb/creator.py:1024
+msgid "Unable to find partition"
+msgstr "Partisyon bulunamadı"
+
+#: ../liveusb/creator.py:1264
+msgid ""
+"Unable to get Win32_LogicalDisk; win32com query did not return any results"
+msgstr "Win32_LogicalDisk alınamadı; win32com sorgusu hiçbir sonuç döndürmedi"
+
+#: ../liveusb/gui.py:671
+msgid "Unable to mount device"
+msgstr "Cihaza bağlanılamadı"
+
+#: ../liveusb/creator.py:785
+#, python-format
+msgid "Unable to mount device: %(message)s"
+msgstr "Cihaz takılamıyor: %(message)s"
+
+#: ../liveusb/creator.py:494
+#, python-format
+msgid "Unable to remove directory from previous LiveOS: %(message)s"
+msgstr "Önceki LiveOS'den klasör silinmesi başarısız: %(message)s"
+
+#: ../liveusb/creator.py:482
+#, python-format
+msgid "Unable to remove file from previous LiveOS: %(message)s"
+msgstr "Önceki LiveOS'den dosya silinmesi başarısız: %(message)s"
+
+#: ../liveusb/creator.py:1099
+msgid ""
+"Unable to reset MBR. You may not have the `syslinux` package installed."
+msgstr "MBR sıfırlanamadı. Bilgisayarınızda 'syslinux' paketi yüklenmemiş olabilir."
+
+#: ../liveusb/gui.py:770
+msgid ""
+"Unable to use the selected file. You may have better luck if you move your "
+"ISO to the root of your drive (ie: C:\\)"
+msgstr "Seçilen dosya kullanılamadı. ISO imajınızı diskinizin kök dizinine <root> taşımayı deneyiniz. (örnek: C:\\)"
+
+#: ../liveusb/creator.py:692
+#, python-format
+msgid "Unable to write on %(device)s, skipping."
+msgstr "%(device)s 'a yazılamıyor, atlanıyor."
+
+#: ../liveusb/creator.py:382
+msgid "Unknown ISO, skipping checksum verification"
+msgstr "Bilinmeyen ISO, checksum onaylanması atlanıyor"
+
+#: ../liveusb/creator.py:781
+#, python-format
+msgid "Unknown dbus exception while trying to mount device: %(message)s"
+msgstr "Cihaz takılmasına ilişkin bilinmeyen dbus istisnası: %(message)s"
+
+#: ../liveusb/creator.py:760 ../liveusb/creator.py:874
+msgid "Unknown filesystem. Your device may need to be reformatted."
+msgstr "Bilinmeyen dosya sistemi. Cihazınızın yeniden formatlanması gerekebilir."
+
+#: ../liveusb/gui.py:84
+#, python-format
+msgid "Unknown release: %s"
+msgstr "Bilinmeyen sürüm: %s"
+
+#: ../liveusb/creator.py:822
+#, python-format
+msgid "Unmounting '%(udi)s' on '%(device)s'"
+msgstr " '%(device)s' dan '%(udi)s' çıkarılıyor."
+
+#: ../liveusb/creator.py:818
+#, python-format
+msgid "Unmounting mounted filesystems on '%(device)s'"
+msgstr " '%(device)s' deki takılı dosya sistemleri çıkarılıyor."
+
+#: ../liveusb/creator.py:765 ../liveusb/creator.py:877
+#, python-format
+msgid "Unsupported filesystem: %s"
+msgstr "Desteklenmeyen dosya sistemi: %s"
+
+#: ../liveusb/creator.py:763
+#, python-format
+msgid ""
+"Unsupported filesystem: %s\n"
+"In case you are trying to upgrade a manually installed Tails system (that is, if it was installed without this installer), this option is not supported: you need to install it anew to start with, e.g. by choosing the \"Clone & Install\" action instead."
+msgstr "Desteklenmeyen dosya sistemi: %s⏎\nEğer elle yüklenmiş olan bir Tails sistemini güncelleştirmeye çalışıyorsanız (yani Tails'i bu kur sihirbazı ile kurmadıysanız) bu seçeneği kullanamazsınız. Yüklemeye baştan başlamanız gerekiyor, örneğin \"Klonla ve Kur\" seçeneğini kullanarak. "
+
+#: ../liveusb/creator.py:1197
+#, python-format
+msgid ""
+"Unsupported filesystem: %s\n"
+"Please backup and format your USB key with the FAT filesystem."
+msgstr "Desteklenmeyen dosya sistemi: %s\nLütfen yedekleyin ve USB belleğinizi FAT dosya sistemi ile biçimlendirin."
+
+#: ../liveusb/launcher_ui.py:154
+msgid "Upgrade from ISO"
+msgstr "ISO'dan güncelleştir"
+
+#: ../liveusb/dialog.py:152
+msgid "Use existing Live system ISO"
+msgstr "Mevcut olan Live sistem ISO'sunu kullan"
+
+#: ../liveusb/creator.py:133
+msgid "Verifying ISO MD5 checksum"
+msgstr "ISO MD5 checksum'ı onaylanıyor"
+
+#: ../liveusb/creator.py:356
+msgid "Verifying SHA1 checksum of LiveCD image..."
+msgstr "LiveCD imaj'ının SHA1 checksum'ı onaylanıyor..."
+
+#: ../liveusb/creator.py:360
+msgid "Verifying SHA256 checksum of LiveCD image..."
+msgstr "LiveCD imajının SHA256 checksum'ı onaylanıyor..."
+
+#: ../liveusb/creator.py:871 ../liveusb/creator.py:1190
+msgid "Verifying filesystem..."
+msgstr "Dosya sistemi onaylanıyor..."
+
+#: ../liveusb/gui.py:677
+msgid "Warning: All data on the selected drive will be lost."
+msgstr "Uyaru: Seçili sürücüdeki tüm veriler silinecektir."
+
+#: ../liveusb/gui.py:694
+msgid ""
+"Warning: Creating a new persistent overlay will delete your existing one."
+msgstr "Uyarı: Yeni bir kalıcı overlay yaratmak mevcut olanın silinmesine neden olacaktır. "
+
+#: ../liveusb/gui.py:657
+msgid ""
+"Warning: The Master Boot Record on your device does not match your system's "
+"syslinux MBR. If you have trouble booting this stick, try running the "
+"liveusb-creator with the --reset-mbr option."
+msgstr "Uyarı: Cihazınızın Master Boot Record'u sisteminizin syslinux Master Boot Record'u ile uyuşmuyor. Bu cihazı başlatmada sorun yaşiyorsaniz, liveusb-creator'ı --reset-mbr seçeneği ile çalıştırmayı deneyiniz."
+
+#: ../liveusb/gui.py:392
+msgid ""
+"Warning: This tool needs to be run as an Administrator. To do this, right "
+"click on the icon and open the Properties. Under the Compatibility tab, "
+"check the \"Run this program as an administrator\" box."
+msgstr "Uyarı: Bu aracı Yönetici olarak çalıştırmak zorundasınız. Bunu yapmak için, simge'ye sağ tıklayın ve Özellikler'i açın. Uyumluluk seçeneği altında, \"Bu programı Yönetici olarak aç\" seçeneğini tıklayın."
+
+#: ../liveusb/creator.py:152
+#, python-format
+msgid "Wrote to device at %(speed)d MB/sec"
+msgstr "Cihaza %(speed)d MB/saniye hızında yazıldı"
+
+#: ../liveusb/creator.py:607
+msgid ""
+"You are using an old version of syslinux-extlinux that does not support the "
+"ext4 filesystem"
+msgstr "syslinux-extlinux'un ext4 dosya sistemini desteklemeyen eski bir versiyonunu kullanıyorsunuz"
+
+#: ../liveusb/gui.py:755
+msgid "You can try again to resume your download"
+msgstr "İndirmeye devam etmeyi yeniden deneyebilirsiniz"
+
+#: ../liveusb/creator.py:92
+msgid "You must run this application as root"
+msgstr "Bu programı root olarak çalıştırmalısınız"
+
+#: ../liveusb/gui.py:691
+msgid ""
+"Your device already contains a LiveOS.\n"
+"If you continue, this will be overwritten."
+msgstr "Cihazınız zaten bir LiveOS barındırıyor.\nDevam ederseniz üzerine yazılacak."
+
+#: ../liveusb/dialog.py:155
+msgid "or"
+msgstr "ya da"
1
0
[translation/liveusb-creator] Update translations for liveusb-creator
by translation@torproject.org 28 Oct '13
by translation@torproject.org 28 Oct '13
28 Oct '13
commit 85b60991d1a3bd18287513f3c8caf5a0043130c5
Author: Translation commit bot <translation(a)torproject.org>
Date: Mon Oct 28 16:45:54 2013 +0000
Update translations for liveusb-creator
---
tr/tr.po | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/tr/tr.po b/tr/tr.po
index f39de2d..678df7e 100644
--- a/tr/tr.po
+++ b/tr/tr.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
"POT-Creation-Date: 2013-09-09 11:21+0200\n"
-"PO-Revision-Date: 2013-10-03 15:20+0000\n"
+"PO-Revision-Date: 2013-10-28 16:40+0000\n"
"Last-Translator: volkangezer <volkangezer(a)gmail.com>\n"
"Language-Team: Turkish (http://www.transifex.com/projects/p/torproject/language/tr/)\n"
"MIME-Version: 1.0\n"
@@ -24,7 +24,7 @@ msgstr ""
#: ../liveusb/dialog.py:150 ../liveusb/launcher_ui.py:149
#, python-format
msgid "%(distribution)s LiveUSB Creator"
-msgstr ""
+msgstr "%(distribution)s ÇalışırUSB Oluşturucu"
#: ../liveusb/gui.py:776
#, python-format
@@ -61,7 +61,7 @@ msgid ""
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Copy the running Tails onto an already installed Tails USB stick. Other partitions found on the stick are preserved.</span></p></body></html>"
-msgstr ""
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Çalışan Tails'i daha önceden kurulmuş Tails USB belleğine kopyala. Bellekte bulunan diğer birimler korunacaktır.</span></p></body></html>"
#: ../liveusb/launcher_ui.py:155
msgid ""
@@ -70,7 +70,7 @@ msgid ""
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Upgrade an already installed Tails USB stick from a new ISO image.</span></p></body></html>"
-msgstr ""
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:10pt;\">Daha önceden kurulmuş Tails USB belleğini, bir ISO kalıbından yükselt.</span></p></body></html>"
#: ../liveusb/dialog.py:154
msgid "Alt+B"
@@ -86,7 +86,7 @@ msgid ""
"will be able to store data and make permanent modifications to your live "
"operating system. Without it, you will not be able to save data that will "
"persist after a reboot."
-msgstr ""
+msgstr "USB belleğinizde kalıcı kaplama için ek alan ayırarak, çalışır işletim sisteminizde kalıcı değişiklikler yapmanızı ve veri depolamanızı mümkün kılarsınız. Bu olmadan, yeniden başlatma işleminden sonra kaydedilen veriler saklanamaz."
#: ../liveusb/creator.py:1106 ../liveusb/creator.py:1369
#, python-format
@@ -123,7 +123,7 @@ msgstr "Live USB oluştur"
#: ../liveusb/creator.py:400
#, python-format
msgid "Creating %sMB persistent overlay"
-msgstr ""
+msgstr "%sMB kalıcı kaplaması oluşturuluyor"
#: ../liveusb/gui.py:556
msgid ""
@@ -150,7 +150,7 @@ msgstr "%s indiriliyor.."
#: ../liveusb/creator.py:1102
msgid "Drive is a loopback, skipping MBR reset"
-msgstr ""
+msgstr "Sürücü bir yansıma, MBR sıfırlama atlanıyor"
#: ../liveusb/creator.py:808
#, python-format
@@ -221,17 +221,17 @@ msgstr "LiveUSB oluşturma başarısız!"
msgid ""
"Make sure to extract the entire liveusb-creator zip file before running this"
" program."
-msgstr ""
+msgstr "Bu programı çalıştırmadan önce liveusb-creator zip dosyasının tamamını çıkarttığınızdan emin olun."
#: ../liveusb/creator.py:1194
msgid ""
"Make sure your USB key is plugged in and formatted with the FAT filesystem"
-msgstr ""
+msgstr "USB belleğinizin takılı ve FAT dosya sistemi ile biçimlendirildiğinden emin olun"
#: ../liveusb/creator.py:830
#, python-format
msgid "Mount %s exists after unmounting"
-msgstr ""
+msgstr "%s bağlantısı ayırma işleminden sonra mevcut"
#: ../liveusb/gui.py:681 ../liveusb/gui.py:698
msgid "Next"
@@ -306,14 +306,14 @@ msgstr "Live ISO seçin"
#: ../liveusb/creator.py:182
msgid "Setting up OLPC boot file..."
-msgstr ""
+msgstr "OLPC önyükleme dosyası ayarlanıyor.."
#: ../liveusb/creator.py:711
#, python-format
msgid ""
"Some partitions of USB device %(device)s are mounted. They will be unmounted"
" before starting the installation process."
-msgstr ""
+msgstr "%(device)s USB aygıtının bazı bölümleri bağlanmış. Kurulum işleminden önce bağlantıları kesilecek."
#: ../liveusb/creator.py:131
msgid ""
@@ -372,16 +372,16 @@ msgstr "Bu Live sisteminizi yüklemek istediğiniz USB cihazı. Bu cihaz FAT dos
msgid ""
"This is the progress bar that will indicate how far along in the LiveUSB "
"creation process you are"
-msgstr ""
+msgstr "Bu ilerleme çubuğu, ÇalışırUSB oluşturma sürecinin hangi adımınızda olduğunu belirtecek"
#: ../liveusb/dialog.py:162
msgid "This is the status console, where all messages get written to."
-msgstr ""
+msgstr "Bu, tüm iletilerin yazılacağı durum konsoludur"
#: ../liveusb/creator.py:895
#, python-format
msgid "Unable to change volume label: %(message)s"
-msgstr ""
+msgstr "Birim etiketi değiştirme başarısız: %(message)s"
#: ../liveusb/creator.py:478 ../liveusb/creator.py:489
#, python-format
@@ -408,7 +408,7 @@ msgstr "Partisyon bulunamadı"
#: ../liveusb/creator.py:1264
msgid ""
"Unable to get Win32_LogicalDisk; win32com query did not return any results"
-msgstr ""
+msgstr "Win32_LogicalDisk alınamadı; win32com sorgusu hiçbir sonuç döndürmedi"
#: ../liveusb/gui.py:671
msgid "Unable to mount device"
@@ -490,7 +490,7 @@ msgstr "Desteklenmeyen dosya sistemi: %s⏎\nEğer elle yüklenmiş olan bir Tai
msgid ""
"Unsupported filesystem: %s\n"
"Please backup and format your USB key with the FAT filesystem."
-msgstr ""
+msgstr "Desteklenmeyen dosya sistemi: %s\nLütfen yedekleyin ve USB belleğinizi FAT dosya sistemi ile biçimlendirin."
#: ../liveusb/launcher_ui.py:154
msgid "Upgrade from ISO"
1
0
28 Oct '13
commit ec742d52b9fad0ac48d1bc139350cd07199fc62a
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Mon Oct 7 12:15:57 2013 +0100
- fix HTTP query string in doc
---
facilitator/facilitator.cgi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/facilitator/facilitator.cgi b/facilitator/facilitator.cgi
index 51a898a..c3eadf4 100755
--- a/facilitator/facilitator.cgi
+++ b/facilitator/facilitator.cgi
@@ -54,7 +54,7 @@ def do_head():
def do_get():
"""Parses flashproxy polls.
- Example: GET /r=1&client=7.1.43.21&client=1.2.3.4&transport=webrtc&transport=websocket
+ Example: GET /?r=1&client=7.1.43.21&client=1.2.3.4&transport=webrtc&transport=websocket
"""
path_parts = [x for x in path_info.split("/") if x]
1
0
[flashproxy/master] symlink hack to make facilitator-test able to import from facilitator
by infinity0@torproject.org 28 Oct '13
by infinity0@torproject.org 28 Oct '13
28 Oct '13
commit 9abbfb811d160db627f5eb50fd64af14bf98e0d7
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Mon Oct 7 13:16:59 2013 +0100
symlink hack to make facilitator-test able to import from facilitator
---
facilitator/facilitator.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/facilitator/facilitator.py b/facilitator/facilitator.py
new file mode 120000
index 0000000..28e5e91
--- /dev/null
+++ b/facilitator/facilitator.py
@@ -0,0 +1 @@
+facilitator
\ No newline at end of file
1
0
[flashproxy/master] fix a bug in get_reg and fix tests to be able to expose that bug
by infinity0@torproject.org 28 Oct '13
by infinity0@torproject.org 28 Oct '13
28 Oct '13
commit f644c0e11362b122bee5e3df6a229057b03e5fb0
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Mon Oct 7 12:33:04 2013 +0100
fix a bug in get_reg and fix tests to be able to expose that bug
---
facilitator/fac.py | 10 ++++++----
facilitator/facilitator-test | 25 +++++++++++++------------
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/facilitator/fac.py b/facilitator/fac.py
index 5e1737a..74ce27d 100644
--- a/facilitator/fac.py
+++ b/facilitator/fac.py
@@ -253,6 +253,8 @@ def transact(f, command, *params):
def put_reg(facilitator_addr, client_addr, transport_chain, registrant_addr=None):
"""Send a registration to the facilitator using a one-time socket. Returns
true iff the command was successful."""
+ # TODO(infinity0): replace "transport_chain" with better terminology, e.g.
+ # transport_pair
f = fac_socket(facilitator_addr)
params = [("CLIENT", format_addr(client_addr))]
params.append(("TRANSPORT_CHAIN", transport_chain))
@@ -275,18 +277,18 @@ def get_reg(facilitator_addr, proxy_addr, transport_list):
"relay-<transport>" if successful, or a dict with the key "client"
mapped to the value "" if there are no registrations available for
proxy_addr. Raises an exception otherwise."""
+ # TODO(infinity0): replace "transport_list" with better terminology, e.g.
+ # transport_suffix_list
f = fac_socket(facilitator_addr)
# Form a list (in transact() format) with the transports that we
# should send to the facilitator. Then pass that list to the
# transact() function.
# For example, TRANSPORT=obfs2 TRANSPORT=obfs3.
- transports = []
- for transport in transport_list:
- transports += ("TRANSPORT", transport)
+ transports = [("TRANSPORT", transport) for transport in transport_list]
try:
- command, params = transact(f, "GET", ("FROM", format_addr(proxy_addr)), transports)
+ command, params = transact(f, "GET", ("FROM", format_addr(proxy_addr)), *transports)
finally:
f.close()
response = {}
diff --git a/facilitator/facilitator-test b/facilitator/facilitator-test
index bd4894e..6b3b0f1 100755
--- a/facilitator/facilitator-test
+++ b/facilitator/facilitator-test
@@ -13,7 +13,8 @@ import fac
FACILITATOR_HOST = "127.0.0.1"
FACILITATOR_PORT = 39002 # diff port to not conflict with production service
FACILITATOR_ADDR = (FACILITATOR_HOST, FACILITATOR_PORT)
-WS_TRANSPORT = ("websocket",)
+CLIENT_TP = "websocket"
+PROXY_TPS = ["websocket", "webrtc"]
def gimme_socket(host, port):
addrinfo = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP)[0]
@@ -81,34 +82,34 @@ class FacilitatorProcTest(unittest.TestCase):
def test_af_v4_v4(self):
"""Test that IPv4 proxies can get IPv4 clients."""
- fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, WS_TRANSPORT)
- fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, WS_TRANSPORT)
- reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR, WS_TRANSPORT)
+ fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, CLIENT_TP)
+ fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, CLIENT_TP)
+ reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR, PROXY_TPS)
self.assertEqual(reg["client"], fac.format_addr(self.IPV4_CLIENT_ADDR))
def test_af_v4_v6(self):
"""Test that IPv4 proxies do not get IPv6 clients."""
- fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, WS_TRANSPORT)
- reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR, WS_TRANSPORT)
+ fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, CLIENT_TP)
+ reg = fac.get_reg(FACILITATOR_ADDR, self.IPV4_PROXY_ADDR, PROXY_TPS)
self.assertEqual(reg["client"], "")
def test_af_v6_v4(self):
"""Test that IPv6 proxies do not get IPv4 clients."""
- fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, WS_TRANSPORT)
- reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, WS_TRANSPORT)
+ fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, CLIENT_TP)
+ reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, PROXY_TPS)
self.assertEqual(reg["client"], "")
def test_af_v6_v6(self):
"""Test that IPv6 proxies can get IPv6 clients."""
- fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, WS_TRANSPORT)
- fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, WS_TRANSPORT)
- reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, WS_TRANSPORT)
+ fac.put_reg(FACILITATOR_ADDR, self.IPV4_CLIENT_ADDR, CLIENT_TP)
+ fac.put_reg(FACILITATOR_ADDR, self.IPV6_CLIENT_ADDR, CLIENT_TP)
+ reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, PROXY_TPS)
self.assertEqual(reg["client"], fac.format_addr(self.IPV6_CLIENT_ADDR))
def test_check_back_in(self):
"""Test that facilitator responses contain a CHECK-BACK-IN key with a
numeric value."""
- reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, WS_TRANSPORT)
+ reg = fac.get_reg(FACILITATOR_ADDR, self.IPV6_PROXY_ADDR, PROXY_TPS)
self.assertGreater(int(reg["check-back-in"]), 0)
# def test_same_proxy(self):
1
0
[flashproxy/master] drop the "chain" terminology and use PROXY_TRANSPORT to describe the proxy's capabilities
by infinity0@torproject.org 28 Oct '13
by infinity0@torproject.org 28 Oct '13
28 Oct '13
commit 011e04c8856fc768e53374bf9c83667ac5d40af1
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Mon Oct 7 16:54:33 2013 +0100
drop the "chain" terminology and use PROXY_TRANSPORT to describe the proxy's capabilities
---
facilitator/fac.py | 12 ++++--------
facilitator/facilitator | 17 ++++++++---------
facilitator/facilitator.cgi | 6 +++---
3 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/facilitator/fac.py b/facilitator/fac.py
index c08ee2a..9cbfa79 100644
--- a/facilitator/fac.py
+++ b/facilitator/fac.py
@@ -250,14 +250,12 @@ def transact(f, command, *params):
raise ValueError("No newline at end of string returned by facilitator: %r" % line)
return parse_transaction(line[:-1])
-def put_reg(facilitator_addr, client_addr, transport_chain, registrant_addr=None):
+def put_reg(facilitator_addr, client_addr, transport, registrant_addr=None):
"""Send a registration to the facilitator using a one-time socket. Returns
true iff the command was successful."""
- # TODO(infinity0): replace "transport_chain" with better terminology, e.g.
- # transport_pair
f = fac_socket(facilitator_addr)
params = [("CLIENT", format_addr(client_addr))]
- params.append(("TRANSPORT_CHAIN", transport_chain))
+ params.append(("TRANSPORT", transport))
if registrant_addr is not None:
params.append(("FROM", format_addr(registrant_addr)))
try:
@@ -266,7 +264,7 @@ def put_reg(facilitator_addr, client_addr, transport_chain, registrant_addr=None
f.close()
return command == "OK"
-def get_reg(facilitator_addr, proxy_addr, transport_list):
+def get_reg(facilitator_addr, proxy_addr, proxy_transport_list):
"""
Get a client registration for proxy 'proxy_addr' from the
facilitator at 'facilitator_addr' using a one-time
@@ -277,15 +275,13 @@ def get_reg(facilitator_addr, proxy_addr, transport_list):
"relay-<transport>" if successful, or a dict with the key "client"
mapped to the value "" if there are no registrations available for
proxy_addr. Raises an exception otherwise."""
- # TODO(infinity0): replace "transport_list" with better terminology, e.g.
- # transport_suffix_list
f = fac_socket(facilitator_addr)
# Form a list (in transact() format) with the transports that we
# should send to the facilitator. Then pass that list to the
# transact() function.
# For example, TRANSPORT=obfs2 TRANSPORT=obfs3.
- transports = [("TRANSPORT", transport) for transport in transport_list]
+ transports = [("PROXY_TRANSPORT", tp) for tp in proxy_transport_list]
try:
command, params = transact(f, "GET", ("FROM", format_addr(proxy_addr)), *transports)
diff --git a/facilitator/facilitator b/facilitator/facilitator
index d4088ff..9c8de92 100755
--- a/facilitator/facilitator
+++ b/facilitator/facilitator
@@ -111,8 +111,7 @@ class Reg(namedtuple("Reg", "addr transport")):
class Endpoints(object):
"""
- Tracks endpoints (either client/server) and the transport chains that
- they support.
+ Tracks endpoints (either client/server) and the transports they support.
"""
matchingLock = threading.Condition()
@@ -357,9 +356,9 @@ class Handler(SocketServer.StreamRequestHandler):
except ValueError as e:
return self.error(u"syntax error in proxy address %s: %%(cause)s" % safe_str(repr(proxy_spec)), e)
- transport_list = fac.param_getlist("TRANSPORT", params)
+ transport_list = fac.param_getlist("PROXY_TRANSPORT", params)
if not transport_list:
- return self.error(u"TRANSPORT missing FROM param")
+ return self.error(u"PROXY_TRANSPORT missing FROM param")
try:
client_reg, relay_reg = get_match_for_proxy(proxy_addr, transport_list)
@@ -382,15 +381,15 @@ class Handler(SocketServer.StreamRequestHandler):
return True
# Handle a PUT request (client made a registration request; register it.)
- # Example: PUT CLIENT="1.1.1.1:5555" TRANSPORT_CHAIN="obfs3|websocket"
+ # Example: PUT CLIENT="1.1.1.1:5555" TRANSPORT="obfs3|websocket"
def do_PUT(self, params):
- # Check out if we recognize the transport chain in this registration request
- transport = fac.param_first("TRANSPORT_CHAIN", params)
+ # Check out if we recognize the transport in this registration request
+ transport = fac.param_first("TRANSPORT", params)
if transport is None:
- return self.error(u"PUT missing TRANSPORT_CHAIN param")
+ return self.error(u"PUT missing TRANSPORT param")
transport = Transport.parse(transport)
- # See if we have relays that support this transport chain
+ # See if we have relays that support this transport
if all(not pts.supports(transport) for pts in SERVERS.itervalues()):
return self.error(u"Unrecognized transport: %s" % transport)
diff --git a/facilitator/facilitator.cgi b/facilitator/facilitator.cgi
index c3eadf4..a68d84e 100755
--- a/facilitator/facilitator.cgi
+++ b/facilitator/facilitator.cgi
@@ -107,9 +107,9 @@ def do_post():
continue
if key == "client": # reg without transport info -- default to websocket.
- transport_chain = "websocket"
+ transport = "websocket"
else: # reg with transport info -- get the "websocket" part out of "client-websocket".
- transport_chain = key[len("client-"):]
+ transport = key[len("client-"):]
# Get the "1.2.3.4:9000" part of "client-websocket=1.2.3.4:9000".
client_spec = fs[key].value.strip()
@@ -124,7 +124,7 @@ def do_post():
# XXX need to link these registrations together, so that
# when one is answerered the rest are invalidated.
- if not fac.put_reg(FACILITATOR_ADDR, client_addr, transport_chain, remote_addr):
+ if not fac.put_reg(FACILITATOR_ADDR, client_addr, transport, remote_addr):
exit_error(500)
print """\
1
0
commit c7b92798bed3875f3c75ef7327d2c9c7a7186bba
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Mon Oct 7 16:47:36 2013 +0100
- note flaky test
---
facilitator/facilitator-test | 1 +
1 file changed, 1 insertion(+)
diff --git a/facilitator/facilitator-test b/facilitator/facilitator-test
index 6b3b0f1..e39cecd 100755
--- a/facilitator/facilitator-test
+++ b/facilitator/facilitator-test
@@ -76,6 +76,7 @@ class FacilitatorProcTest(unittest.TestCase):
while buflen + 1024 < 200000:
s.send("X" * 1024)
buflen += 1024
+ # TODO(dcf1): sometimes no error is raised, and this test fails
self.fail("should have raised a socket error")
except socket.error:
pass
1
0
[flashproxy/master] report more detailed errors and show stacktraces when unsafe logging
by infinity0@torproject.org 28 Oct '13
by infinity0@torproject.org 28 Oct '13
28 Oct '13
commit c1af35b2e2d57cb8850b1cf345d29065766a9410
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Mon Oct 7 13:07:20 2013 +0100
report more detailed errors and show stacktraces when unsafe logging
---
facilitator/fac.py | 6 ++---
facilitator/facilitator | 63 +++++++++++++++++++----------------------------
2 files changed, 28 insertions(+), 41 deletions(-)
diff --git a/facilitator/fac.py b/facilitator/fac.py
index 74ce27d..c08ee2a 100644
--- a/facilitator/fac.py
+++ b/facilitator/fac.py
@@ -196,7 +196,7 @@ def parse_transaction(line):
if not (pos < len(line)):
break
if skipped == 0:
- raise ValueError("Expected space before key-value pair")
+ raise ValueError("Expected space before key-value pair: %r" % line)
pos, key = get_token(pos, line)
if not (pos < len(line) and line[pos] == '='):
raise ValueError("No '=' found after key")
@@ -247,7 +247,7 @@ def transact(f, command, *params):
f.flush()
line = f.readline()
if not (len(line) > 0 and line[-1] == '\n'):
- raise ValueError("No newline at end of string returned by facilitator")
+ raise ValueError("No newline at end of string returned by facilitator: %r" % line)
return parse_transaction(line[:-1])
def put_reg(facilitator_addr, client_addr, transport_chain, registrant_addr=None):
@@ -316,7 +316,7 @@ def get_reg(facilitator_addr, proxy_addr, transport_list):
response["relay"] = format_addr(relay)
return response
else:
- raise ValueError("Facilitator response was not \"OK\"")
+ raise ValueError("Facilitator response was not \"OK\": %s: %s" % (command, params))
def put_reg_base64(b64):
"""Attempt to add a registration by running a facilitator-reg program
diff --git a/facilitator/facilitator b/facilitator/facilitator
index 647e1ed..e44047e 100755
--- a/facilitator/facilitator
+++ b/facilitator/facilitator
@@ -7,6 +7,7 @@ import socket
import sys
import threading
import time
+import traceback
import fac
@@ -228,7 +229,7 @@ class Handler(SocketServer.StreamRequestHandler):
if not line:
break
num_lines += 1
- except socket.error, e:
+ except socket.error as e:
log("socket error after reading %d lines: %s" % (num_lines, str(e)))
break
if not self.handle_line(line):
@@ -239,52 +240,46 @@ class Handler(SocketServer.StreamRequestHandler):
raise ValueError("No newline at end of string returned by readline")
try:
command, params = fac.parse_transaction(line[:-1])
- except ValueError, e:
- log("fac.parse_transaction: %s" % e)
- self.send_error()
- return False
+ except ValueError as e:
+ return self.error("fac.parse_transaction: %(cause)s", e)
if command == "GET":
return self.do_GET(params)
elif command == "PUT":
return self.do_PUT(params)
else:
- self.send_error()
- return False
+ return self.error(u"unrecognized command: %s" % command)
def send_ok(self):
print >> self.wfile, "OK"
- def send_error(self):
- print >> self.wfile, "ERROR"
+ def error(self, msg="", cause=None):
+ msg = safe_str(msg % { "cause": cause })
+ log(msg)
+ if not options.safe_logging:
+ log(traceback.format_exc())
+ print >>self.wfile, fac.render_transaction("ERROR", ("MSG", msg))
+ return False
# Handle a GET request (got flashproxy poll; need to return a proper client registration)
# Example: GET FROM="3.3.3.3:3333" TRANSPORT="websocket" TRANSPORT="webrtc"
def do_GET(self, params):
proxy_spec = fac.param_first("FROM", params)
if proxy_spec is None:
- log(u"GET missing FROM param")
- self.send_error()
- return False
+ return self.error(u"GET missing FROM param")
try:
proxy_addr = fac.parse_addr_spec(proxy_spec, defport=0)
- except ValueError, e:
- log(u"syntax error in proxy address %s: %s" % (safe_str(repr(proxy_spec)), safe_str(repr(str(e)))))
- self.send_error()
- return False
+ except ValueError as e:
+ return self.error(u"syntax error in proxy address %s: %%(cause)s" % safe_str(repr(proxy_spec)), e)
transport_list = fac.param_getlist("TRANSPORT", params)
if not transport_list:
- log(u"TRANSPORT missing FROM param")
- self.send_error()
- return False
+ return self.error(u"TRANSPORT missing FROM param")
try:
reg = get_reg_for_proxy(proxy_addr, transport_list)
- except Exception, e:
- log(u"error getting reg for proxy address %s: %s" % (safe_str(repr(proxy_spec)), safe_str(repr(str(e)))))
- self.send_error()
- return False
+ except Exception as e:
+ return self.error(u"error getting match for proxy address %s: %%(cause)s" % safe_str(repr(proxy_spec)), e)
check_back_in = get_check_back_in_for_proxy(proxy_addr)
@@ -299,14 +294,12 @@ class Handler(SocketServer.StreamRequestHandler):
return True
# Handle a PUT request (client made a registration request; register it.)
- # Example: PUT CLIENT="1.1.1.1:5555" FROM="1.1.1.2:6666" TRANSPORT_CHAIN="obfs3|websocket"
+ # Example: PUT CLIENT="1.1.1.1:5555" TRANSPORT_CHAIN="obfs3|websocket"
def do_PUT(self, params):
# Check out if we recognize the transport chain in this registration request
transports_spec = fac.param_first("TRANSPORT_CHAIN", params)
if transports_spec is None:
- log(u"PUT missing TRANSPORT_CHAIN param")
- self.send_error()
- return False
+ return self.error(u"PUT missing TRANSPORT_CHAIN param")
transports = parse_transport_chain(transports_spec)
@@ -321,24 +314,18 @@ class Handler(SocketServer.StreamRequestHandler):
client_spec = fac.param_first("CLIENT", params)
if client_spec is None:
- log(u"PUT missing CLIENT param")
- self.send_error()
- return False
+ return self.error(u"PUT missing CLIENT param")
try:
reg = Reg.parse(client_spec, transports)
except (UnknownTransport, ValueError) as e:
# XXX should we throw a better error message to the client? Is it possible?
- log(u"syntax error in %s: %s" % (safe_str(repr(client_spec)), safe_str(repr(str(e)))))
- self.send_error()
- return False
+ return self.error(u"syntax error in %s: %%(cause)s" % safe_str(repr(client_spec)), e)
try:
ok = put_reg(reg)
- except Exception, e:
- log(u"error putting reg %s: %s" % (safe_str(repr(client_spec)), safe_str(repr(str(e)))))
- self.send_error()
- return False
+ except Exception as e:
+ return self.error(u"error putting reg %s: %%(cause)s" % safe_str(repr(client_spec)), e)
if ok:
log(u"client %s (transports: %s) (remaining regs: %d/%d)" % (safe_str(unicode(reg)), reg.transports, num_unhandled_regs(), num_regs()))
@@ -528,7 +515,7 @@ obfs2|websocket 1.4.6.1:4123\
log(u"dropping privileges to those of user %s" % options.privdrop_username)
try:
fac.drop_privs(options.privdrop_username)
- except BaseException, e:
+ except BaseException as e:
print >> sys.stderr, "Can't drop privileges:", str(e)
sys.exit(1)
1
0
[flashproxy/master] Move util classes to fac.py since they'll be needed later, and rename "Reg" to more general "Endpoint"
by infinity0@torproject.org 28 Oct '13
by infinity0@torproject.org 28 Oct '13
28 Oct '13
commit e9bdc091ca2c35f013f338552ea01cf966806eb9
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Tue Oct 8 16:50:46 2013 +0100
Move util classes to fac.py since they'll be needed later, and rename "Reg" to more general "Endpoint"
---
facilitator/fac.py | 31 +++++++++++++++++++++++++++++++
facilitator/facilitator | 35 +++--------------------------------
facilitator/facilitator-test | 15 ++++++++-------
3 files changed, 42 insertions(+), 39 deletions(-)
diff --git a/facilitator/fac.py b/facilitator/fac.py
index 9cbfa79..cedadd8 100644
--- a/facilitator/fac.py
+++ b/facilitator/fac.py
@@ -5,6 +5,7 @@ import socket
import stat
import subprocess
import pwd
+from collections import namedtuple
# Return true iff the given fd is readable, writable, and executable only by its
# owner.
@@ -146,6 +147,36 @@ def format_addr(addr):
raise ValueError("host and port may not both be None")
return u"%s%s" % (host_str, port_str)
+
+class Transport(namedtuple("Transport", "prefix suffix")):
+ @classmethod
+ def parse(cls, transport):
+ if isinstance(transport, cls):
+ return transport
+ elif type(transport) == str:
+ if "|" in transport:
+ prefix, suffix = transport.rsplit("|", 1)
+ else:
+ prefix, suffix = "", transport
+ return cls(prefix, suffix)
+ else:
+ raise ValueError("could not parse transport: %s" % transport)
+
+ def __init__(self, prefix, suffix):
+ if not suffix:
+ raise ValueError("suffix (proxy) part of transport must be non-empty: %s" % str(self))
+
+ def __str__(self):
+ return "%s|%s" % (self.prefix, self.suffix) if self.prefix else self.suffix
+
+
+class Endpoint(namedtuple("Endpoint", "addr transport")):
+ @classmethod
+ def parse(cls, spec, transport, defhost = None, defport = None):
+ host, port = parse_addr_spec(spec, defhost, defport)
+ return cls((host, port), Transport.parse(transport))
+
+
def skip_space(pos, line):
"""Skip a (possibly empty) sequence of space characters (the ASCII character
'\x20' exactly). Returns a pair (pos, num_skipped)."""
diff --git a/facilitator/facilitator b/facilitator/facilitator
index 9c8de92..f3ae79b 100755
--- a/facilitator/facilitator
+++ b/facilitator/facilitator
@@ -8,9 +8,9 @@ import sys
import threading
import time
import traceback
-from collections import namedtuple
import fac
+from fac import Transport, Endpoint
LISTEN_ADDRESS = "127.0.0.1"
DEFAULT_LISTEN_PORT = 9002
@@ -80,35 +80,6 @@ def log(msg):
options.log_file.flush()
-class Transport(namedtuple("Transport", "prefix suffix")):
- @classmethod
- def parse(cls, transport):
- if isinstance(transport, cls):
- return transport
- elif type(transport) == str:
- if "|" in transport:
- prefix, suffix = transport.rsplit("|", 1)
- else:
- prefix, suffix = "", transport
- return cls(prefix, suffix)
- else:
- raise ValueError("could not parse transport: %s" % transport)
-
- def __init__(self, prefix, suffix):
- if not suffix:
- raise ValueError("suffix (proxy) part of transport must be non-empty: %s" % str(self))
-
- def __str__(self):
- return "%s|%s" % (self.prefix, self.suffix) if self.prefix else self.suffix
-
-
-class Reg(namedtuple("Reg", "addr transport")):
- @classmethod
- def parse(cls, spec, transport, defhost = None, defport = None):
- host, port = fac.parse_addr_spec(spec, defhost, defport)
- return cls((host, port), Transport.parse(transport))
-
-
class Endpoints(object):
"""
Tracks endpoints (either client/server) and the transports they support.
@@ -261,7 +232,7 @@ class Endpoints(object):
# assume servers never run out
client_transport = ptsClient._endpoints[client_addr]
server_transport = ptsServer._endpoints[server_addr]
- return Reg(client_addr, client_transport), Reg(server_addr, server_transport)
+ return Endpoint(client_addr, client_transport), Endpoint(server_addr, server_transport)
class Handler(SocketServer.StreamRequestHandler):
@@ -398,7 +369,7 @@ class Handler(SocketServer.StreamRequestHandler):
return self.error(u"PUT missing CLIENT param")
try:
- reg = Reg.parse(client_spec, transport)
+ reg = Endpoint.parse(client_spec, transport)
except (UnknownTransport, ValueError) as e:
# XXX should we throw a better error message to the client? Is it possible?
return self.error(u"syntax error in %s: %%(cause)s" % safe_str(repr(client_spec)), e)
diff --git a/facilitator/facilitator-test b/facilitator/facilitator-test
index b81b84e..6709221 100755
--- a/facilitator/facilitator-test
+++ b/facilitator/facilitator-test
@@ -7,8 +7,9 @@ import tempfile
import time
import unittest
-from facilitator import Transport, Reg, Endpoints, parse_relay_file
+from facilitator import Endpoints, parse_relay_file
import fac
+from fac import Transport, Endpoint
FACILITATOR_HOST = "127.0.0.1"
FACILITATOR_PORT = 39002 # diff port to not conflict with production service
@@ -114,7 +115,7 @@ class EndpointsTest(unittest.TestCase):
self.pts2.addEndpoint("B", "a|p")
self.pts2.addEndpoint("C", "b|p")
self.pts2.addEndpoint("D", "a|q")
- expected = (Reg("A", Transport("a","p")), Reg("B", Transport("a","p")))
+ expected = (Endpoint("A", Transport("a","p")), Endpoint("B", Transport("a","p")))
empty = Endpoints.EMPTY_MATCH
self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p"]))
self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
@@ -123,7 +124,7 @@ class EndpointsTest(unittest.TestCase):
self.pts.addEndpoint("A", "a|p")
self.pts2 = Endpoints(af=socket.AF_INET)
self.pts2.addEndpoint("B", "a|q")
- expected = (Reg("A", Transport("a","p")), Reg("B", Transport("a","q")))
+ expected = (Endpoint("A", Transport("a","p")), Endpoint("B", Transport("a","q")))
empty = Endpoints.EMPTY_MATCH
self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p", "q"]))
self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["p"]))
@@ -134,7 +135,7 @@ class EndpointsTest(unittest.TestCase):
self.pts.addEndpoint("A", "p")
self.pts2 = Endpoints(af=socket.AF_INET)
self.pts2.addEndpoint("B", "p")
- expected = (Reg("A", Transport("","p")), Reg("B", Transport("","p")))
+ expected = (Endpoint("A", Transport("","p")), Endpoint("B", Transport("","p")))
empty = Endpoints.EMPTY_MATCH
self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p"]))
self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
@@ -149,9 +150,9 @@ class EndpointsTest(unittest.TestCase):
self.pts2.addEndpoint("F", "p")
# this test ensures we have a sane policy for selecting between prefix pools
expected = set()
- expected.add((Reg("A", Transport("a","p")), Reg("D", Transport("a","p"))))
- expected.add((Reg("B", Transport("b","p")), Reg("E", Transport("b","p"))))
- expected.add((Reg("C", Transport("","p")), Reg("F", Transport("","p"))))
+ expected.add((Endpoint("A", Transport("a","p")), Endpoint("D", Transport("a","p"))))
+ expected.add((Endpoint("B", Transport("b","p")), Endpoint("E", Transport("b","p"))))
+ expected.add((Endpoint("C", Transport("","p")), Endpoint("F", Transport("","p"))))
result = set()
result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
1
0
[flashproxy/master] Reimplement matching algorithm using an Endpoints datastruct for both client/server
by infinity0@torproject.org 28 Oct '13
by infinity0@torproject.org 28 Oct '13
28 Oct '13
commit 2658f6e3eac20a4f7c20e2c6b90ddf253731dce1
Author: Ximin Luo <infinity0(a)gmx.com>
Date: Mon Oct 7 16:47:53 2013 +0100
Reimplement matching algorithm using an Endpoints datastruct for both client/server
- select prefix pool based on least-well-served rather than arbitrarily
- don't attempt a match until a proxy is available to service the request
- don't match ipv6 proxies to ipv4 servers
---
facilitator/facilitator | 428 +++++++++++++++++++++++-------------------
facilitator/facilitator-test | 166 +++++++++++++++-
2 files changed, 396 insertions(+), 198 deletions(-)
diff --git a/facilitator/facilitator b/facilitator/facilitator
index e44047e..d4088ff 100755
--- a/facilitator/facilitator
+++ b/facilitator/facilitator
@@ -8,6 +8,7 @@ import sys
import threading
import time
import traceback
+from collections import namedtuple
import fac
@@ -65,26 +66,6 @@ again. Listen on 127.0.0.1 and port PORT (by default %(port)d).
"log": DEFAULT_LOG_FILENAME,
}
-def num_relays():
- return sum(len(x) for x in RELAYS.values())
-
-def parse_transport_chain(spec):
- """Parse a transport chain string and return a tuple of individual
- transports, each of which is a string.
- >>> parse_transport_chain("obfs3|websocket")
- ('obfs3', 'websocket')
- """
- assert(spec)
- return tuple(spec.split("|"))
-
-def get_outermost_transport(transports):
- """Given a transport chain tuple, return the last element.
- >>> get_outermost_transport(("obfs3", "websocket"))
- 'websocket'
- """
- assert(transports)
- return transports[-1]
-
def safe_str(s):
"""Return "[scrubbed]" if options.safe_logging is true, and s otherwise."""
if options.safe_logging:
@@ -98,87 +79,191 @@ def log(msg):
print >> options.log_file, (u"%s %s" % (time.strftime(LOG_DATE_FORMAT), msg)).encode("UTF-8")
options.log_file.flush()
-class TCPReg(object):
- def __init__(self, host, port, transports):
- self.host = host
- self.port = port
- self.transports = transports
- # Get a relay for this registration. Throw UnknownTransport if
- # could not be found.
- self.relay = self._get_matching_relay()
- def _get_matching_relay(self):
- """Return a matching relay address for this registration. Raise
- UnknownTransport if a relay with a matching transport chain could not be
- found."""
- if self.transports not in RELAYS:
- raise UnknownTransport("Can't find relay with transport chain: %s" % self.transports)
-
- # Maybe this should be a random pick from the set of all the
- # eligible relays. But let's keep it deterministic for now,
- # and return the first one.
-
- # return random.choice(RELAYS[self.transports])
- return RELAYS[self.transports][0]
+class Transport(namedtuple("Transport", "prefix suffix")):
+ @classmethod
+ def parse(cls, transport):
+ if isinstance(transport, cls):
+ return transport
+ elif type(transport) == str:
+ if "|" in transport:
+ prefix, suffix = transport.rsplit("|", 1)
+ else:
+ prefix, suffix = "", transport
+ return cls(prefix, suffix)
+ else:
+ raise ValueError("could not parse transport: %s" % transport)
- def __unicode__(self):
- return fac.format_addr((self.host, self.port))
+ def __init__(self, prefix, suffix):
+ if not suffix:
+ raise ValueError("suffix (proxy) part of transport must be non-empty: %s" % str(self))
def __str__(self):
- return unicode(self).encode("UTF-8")
+ return "%s|%s" % (self.prefix, self.suffix) if self.prefix else self.suffix
- def __cmp__(self, other):
- if isinstance(other, TCPReg):
- # XXX is this correct comparison?
- return cmp((self.host, self.port, self.transports), (other.host, other.port, other.transports))
- else:
- return False
-class Reg(object):
- @staticmethod
- def parse(spec, transports, defhost = None, defport = None):
+class Reg(namedtuple("Reg", "addr transport")):
+ @classmethod
+ def parse(cls, spec, transport, defhost = None, defport = None):
host, port = fac.parse_addr_spec(spec, defhost, defport)
- return TCPReg(host, port, transports)
+ return cls((host, port), Transport.parse(transport))
-class RegSet(object):
- def __init__(self):
- self.tiers = [[] for i in range(MAX_PROXIES_PER_CLIENT)]
- self.cv = threading.Condition()
- def add(self, reg):
- self.cv.acquire()
- try:
- for tier in self.tiers:
- if reg in tier:
- break
- else:
- self.tiers[0].append(reg)
- self.cv.notify()
- return True
- return False
- finally:
- self.cv.release()
-
- def fetch(self):
- self.cv.acquire()
- try:
- for i in range(len(self.tiers)):
- tier = self.tiers[i]
- if tier:
- reg = tier.pop(0)
- if i + 1 < len(self.tiers):
- self.tiers[i+1].append(reg)
- return reg
+class Endpoints(object):
+ """
+ Tracks endpoints (either client/server) and the transport chains that
+ they support.
+ """
+
+ matchingLock = threading.Condition()
+
+ def __init__(self, af, maxserve=float("inf"), known_suf=("websocket",)):
+ self.af = af
+ self._maxserve = maxserve
+ self._endpoints = {} # address -> transport
+ self._indexes = {} # suffix -> [ addresses ]
+ self._served = {} # address -> num_times_served
+ self._cv = threading.Condition()
+ self.known_suf = set(known_suf)
+ for suf in self.known_suf:
+ self._ensureIndexForSuffix(suf)
+
+ def getNumEndpoints(self):
+ """:returns: the number of endpoints known to us."""
+ with self._cv:
+ return len(self._endpoints)
+
+ def getNumUnservedEndpoints(self):
+ """:returns: the number of unserved endpoints known to us."""
+ with self._cv:
+ return len(filter(lambda t: t == 0, self._served.itervalues()))
+
+ def addEndpoint(self, addr, transport):
+ """Add an endpoint.
+
+ :param addr: Address of endpoint, usage-dependent.
+ :param list transports: List of transports.
+ :returns: False if the address is already known, in which case no
+ update is made to its supported transports, else True.
+ """
+ transport = Transport.parse(transport)
+ with self._cv:
+ if addr in self._endpoints: return False
+ self._endpoints[addr] = transport
+ self._served[addr] = 0
+ self._addAddrIntoIndexes(addr)
+ self._cv.notify()
+ return True
+
+ def delEndpoint(self, addr):
+ """Forget an endpoint.
+
+ :param addr: Address of endpoint, usage-dependent.
+ :returns: False if the address was already forgotten, else True.
+ """
+ with self._cv:
+ if addr not in self._endpoints: return False
+ self._delAddrFromIndexes(addr)
+ del self._served[addr]
+ del self._endpoints[addr]
+ self._cv.notify()
+ return True
+
+ def supports(self, transport):
+ """
+ Estimate whether we support the given transport. May give false
+ positives, but doing a proper match later on will catch these.
+
+ :returns: True if we know, or have met, proxies that might be able
+ to satisfy the requested transport against our known endpoints.
+ """
+ transport = Transport.parse(transport)
+ with self._cv:
+ known_pre = self._findPrefixesForSuffixes(*self.known_suf).keys()
+ pre, suf = transport.prefix, transport.suffix
+ return pre in known_pre and suf in self.known_suf
+
+ def _findPrefixesForSuffixes(self, *supported_suf):
+ """
+ :returns: { prefix: [addr] }, where each address supports some suffix
+ from supported_suf. TODO(infinity0): describe better
+ """
+ self.known_suf.update(supported_suf)
+ prefixes = {}
+ for suf in supported_suf:
+ self._ensureIndexForSuffix(suf)
+ for addr in self._indexes[suf]:
+ pre = self._endpoints[addr].prefix
+ prefixes.setdefault(pre, set()).add(addr)
+ return prefixes
+
+ def _avServed(self, addrpool):
+ return sum(self._served[a] for a in addrpool) / float(len(addrpool))
+
+ def _serveReg(self, addrpool):
+ """
+ :param list addrpool: List of candidate addresses.
+ :returns: An address of an endpoint from the given pool, or None if all
+ endpoints have already been served _maxserve times. The serve
+ counter for that address is also incremented.
+ """
+ if not addrpool: return None
+ prio_addr = min(addrpool, key=lambda a: self._served[a])
+ if self._served[prio_addr] < self._maxserve:
+ self._served[prio_addr] += 1
+ return prio_addr
+ else:
return None
- finally:
- self.cv.release()
- def __len__(self):
- self.cv.acquire()
- try:
- return sum(len(tier) for tier in self.tiers)
- finally:
- self.cv.release()
+ def _ensureIndexForSuffix(self, suf):
+ if suf in self._indexes: return
+ addrs = set(addr for addr, transport in self._endpoints.iteritems()
+ if transport.suffix == suf)
+ self._indexes[suf] = addrs
+
+ def _addAddrIntoIndexes(self, addr):
+ suf = self._endpoints[addr].suffix
+ if suf in self._indexes: self._indexes[suf].add(addr)
+
+ def _delAddrFromIndexes(self, addr):
+ suf = self._endpoints[addr].suffix
+ if suf in self._indexes: self._indexes[suf].remove(addr)
+
+ def _prefixesForTransport(self, transport, *supported_suf):
+ for suf in supported_suf:
+ if not suf:
+ yield transport
+ elif transport[-len(suf):] == suf:
+ yield transport[:-len(suf)]
+
+ EMPTY_MATCH = (None, None)
+ @staticmethod
+ def match(ptsClient, ptsServer, supported_suf):
+ """
+ :returns: A tuple (client Reg, server Reg) arbitrarily selected from
+ the available endpoints that can satisfy supported_suf.
+ """
+ if ptsClient.af != ptsServer.af:
+ raise ValueError("address family not equal!")
+ # need to operate on both structures
+ # so hold both locks plus a pair-wise lock
+ with Endpoints.matchingLock, ptsClient._cv, ptsServer._cv:
+ server_pre = ptsServer._findPrefixesForSuffixes(*supported_suf)
+ client_pre = ptsClient._findPrefixesForSuffixes(*supported_suf)
+ both = set(server_pre.keys()) & set(client_pre.keys())
+ if not both: return Endpoints.EMPTY_MATCH
+ # pick the prefix whose client address pool is least well-served
+ # TODO: this may be manipulated by clients, needs research
+ assert all(client_pre.itervalues()) # no pool is empty
+ pre = min(both, key=lambda p: ptsClient._avServed(client_pre[p]))
+ client_addr = ptsClient._serveReg(client_pre[pre])
+ if not client_addr: return Endpoints.EMPTY_MATCH
+ server_addr = ptsServer._serveReg(server_pre[pre])
+ # assume servers never run out
+ client_transport = ptsClient._endpoints[client_addr]
+ server_transport = ptsServer._endpoints[server_addr]
+ return Reg(client_addr, client_transport), Reg(server_addr, server_transport)
+
class Handler(SocketServer.StreamRequestHandler):
def __init__(self, *args, **kwargs):
@@ -277,16 +362,19 @@ class Handler(SocketServer.StreamRequestHandler):
return self.error(u"TRANSPORT missing FROM param")
try:
- reg = get_reg_for_proxy(proxy_addr, transport_list)
+ client_reg, relay_reg = get_match_for_proxy(proxy_addr, transport_list)
except Exception as e:
return self.error(u"error getting match for proxy address %s: %%(cause)s" % safe_str(repr(proxy_spec)), e)
check_back_in = get_check_back_in_for_proxy(proxy_addr)
- if reg:
- log(u"proxy (%s) gets client '%s' (transports: %s) (num relays: %s) (remaining regs: %d/%d)" %
- (safe_str(repr(proxy_spec)), safe_str(unicode(reg)), reg.transports, num_relays(), num_unhandled_regs(), num_regs()))
- print >> self.wfile, fac.render_transaction("OK", ("CLIENT", str(reg)), ("RELAY", fac.format_addr(reg.relay)), ("CHECK-BACK-IN", str(check_back_in)))
+ if client_reg:
+ log(u"proxy (%s) gets client '%s' (supported transports: %s) (num relays: %s) (remaining regs: %d/%d)" %
+ (safe_str(repr(proxy_spec)), safe_str(repr(client_reg.addr)), transport_list, num_relays(), num_unhandled_regs(), num_regs()))
+ print >> self.wfile, fac.render_transaction("OK",
+ ("CLIENT", fac.format_addr(client_reg.addr)),
+ ("RELAY", fac.format_addr(relay_reg.addr)),
+ ("CHECK-BACK-IN", str(check_back_in)))
else:
log(u"proxy (%s) gets none" % safe_str(repr(proxy_spec)))
print >> self.wfile, fac.render_transaction("NONE", ("CHECK-BACK-IN", str(check_back_in)))
@@ -297,27 +385,21 @@ class Handler(SocketServer.StreamRequestHandler):
# Example: PUT CLIENT="1.1.1.1:5555" TRANSPORT_CHAIN="obfs3|websocket"
def do_PUT(self, params):
# Check out if we recognize the transport chain in this registration request
- transports_spec = fac.param_first("TRANSPORT_CHAIN", params)
- if transports_spec is None:
+ transport = fac.param_first("TRANSPORT_CHAIN", params)
+ if transport is None:
return self.error(u"PUT missing TRANSPORT_CHAIN param")
- transports = parse_transport_chain(transports_spec)
-
+ transport = Transport.parse(transport)
# See if we have relays that support this transport chain
- if transports not in RELAYS:
- log(u"Unrecognized transport chain: %s" % transports)
- self.send_error() # XXX can we tell the flashproxy client of this error?
- return False
- # if we have relays that support this transport chain, we
- # certainly have a regset for its outermost transport too.
- assert(get_outermost_transport(transports) in REGSETS_IPV4)
+ if all(not pts.supports(transport) for pts in SERVERS.itervalues()):
+ return self.error(u"Unrecognized transport: %s" % transport)
client_spec = fac.param_first("CLIENT", params)
if client_spec is None:
return self.error(u"PUT missing CLIENT param")
try:
- reg = Reg.parse(client_spec, transports)
+ reg = Reg.parse(client_spec, transport)
except (UnknownTransport, ValueError) as e:
# XXX should we throw a better error message to the client? Is it possible?
return self.error(u"syntax error in %s: %%(cause)s" % safe_str(repr(client_spec)), e)
@@ -328,9 +410,9 @@ class Handler(SocketServer.StreamRequestHandler):
return self.error(u"error putting reg %s: %%(cause)s" % safe_str(repr(client_spec)), e)
if ok:
- log(u"client %s (transports: %s) (remaining regs: %d/%d)" % (safe_str(unicode(reg)), reg.transports, num_unhandled_regs(), num_regs()))
+ log(u"client %s (transports: %s) (remaining regs: %d/%d)" % (safe_str(unicode(reg)), reg.transport, num_unhandled_regs(), num_regs()))
else:
- log(u"client %s (already present) (transports: %s) (remaining regs: %d/%d)" % (safe_str(unicode(reg)), reg.transports, num_unhandled_regs(), num_regs()))
+ log(u"client %s (already present) (transports: %s) (remaining regs: %d/%d)" % (safe_str(unicode(reg)), reg.transport, num_unhandled_regs(), num_regs()))
self.send_ok()
return True
@@ -341,48 +423,29 @@ class Server(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
allow_reuse_address = True
# Registration sets per-outermost-transport
-# {"websocket" : <RegSet for websocket>, "webrtc" : <RegSet for webrtc>}
-REGSETS_IPV4 = {}
-REGSETS_IPV6 = {}
+# Addresses are plain tuples (str(host), int(port))
-def num_regs():
- """Return the total number of registrations."""
- num_regs = 0
+CLIENTS = {
+ socket.AF_INET: Endpoints(af=socket.AF_INET, maxserve=MAX_PROXIES_PER_CLIENT),
+ socket.AF_INET6: Endpoints(af=socket.AF_INET6, maxserve=MAX_PROXIES_PER_CLIENT)
+}
+
+SERVERS = {
+ socket.AF_INET: Endpoints(af=socket.AF_INET),
+ socket.AF_INET6: Endpoints(af=socket.AF_INET6)
+}
- # Iterate the regsets of each regset-dictionary, and count their
- # registrations.
- for regset in REGSETS_IPV4.values():
- num_regs += len(regset)
- for regset in REGSETS_IPV6.values():
- num_regs += len(regset)
+def num_relays():
+ """Return the total number of relays."""
+ return sum(pts.getNumEndpoints() for pts in SERVERS.itervalues())
- return num_regs
+def num_regs():
+ """Return the total number of registrations."""
+ return sum(pts.getNumEndpoints() for pts in CLIENTS.itervalues())
def num_unhandled_regs():
"""Return the total number of unhandled registrations."""
- num_regs = 0
-
- # Iterate the regsets of each regset-dictionary, and count their
- # unhandled registrations. The first tier of each regset contains
- # the registrations with no assigned proxy.
- for regset in REGSETS_IPV4.values():
- num_regs += len(regset.tiers[0])
- for regset in REGSETS_IPV6.values():
- num_regs += len(regset.tiers[0])
-
- return num_regs
-
-def get_regs(af, transport):
- """Return the correct regs pool for the given address family and transport."""
- if transport not in REGSETS_IPV4:
- raise UnknownTransport("unknown transport '%s'" % transport)
-
- if af == socket.AF_INET:
- return REGSETS_IPV4[transport]
- elif af == socket.AF_INET6:
- return REGSETS_IPV6[transport]
- else:
- raise ValueError("unknown address family %d" % af)
+ return sum(pts.getNumUnservedEndpoints() for pts in CLIENTS.itervalues())
def addr_af(addr_str):
"""Return the address family for an address string. This is a plain string,
@@ -390,26 +453,12 @@ def addr_af(addr_str):
addrs = socket.getaddrinfo(addr_str, 0, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST)
return addrs[0][0]
-def get_reg_for_proxy(proxy_addr, transport_list):
- """Get a client registration appropriate for the given proxy (one
- of a matching address family). If 'transports' is set, try to find
- a client registration that supports the outermost transport of a
- transport chain."""
- # XXX How should we prioritize transport matching? We currently
- # just iterate the transport list that was provided by the flashproxy
- for transport in transport_list:
- addr_str = proxy_addr[0]
- af = addr_af(addr_str)
-
- try:
- REGS = get_regs(af, transport)
- except UnknownTransport as e:
- log(u"%s" % e)
- continue # move to the next transport
-
- return REGS.fetch()
-
- raise UnknownTransport("Could not find registration for transport list: %s" % str(transport_list))
+def get_match_for_proxy(proxy_addr, transport_list):
+ af = addr_af(proxy_addr[0])
+ try:
+ return Endpoints.match(CLIENTS[af], SERVERS[af], transport_list)
+ except ValueError as e:
+ raise UnknownTransport("Could not find registration for transport list: %s: %s" % (transport_list, e))
def get_check_back_in_for_proxy(proxy_addr):
"""Get a CHECK-BACK-IN interval suitable for this proxy."""
@@ -417,29 +466,24 @@ def get_check_back_in_for_proxy(proxy_addr):
def put_reg(reg):
"""Add a registration."""
- addr_str = reg.host
- af = addr_af(addr_str)
- REGS = get_regs(af, get_outermost_transport(reg.transports))
- return REGS.add(reg)
+ af = addr_af(reg.addr[0])
+ return CLIENTS[af].addEndpoint(reg.addr, reg.transport)
-def parse_relay_file(filename):
+def parse_relay_file(servers, fp):
"""Parse a file containing Tor relays that we can point proxies to.
Throws ValueError on a parsing error. Each line contains a transport chain
and an address, for example
obfs2|websocket 1.4.6.1:4123
"""
- relays = {}
- with open(filename) as f:
- for line in f:
- try:
- transport_spec, addr_spec = line.strip().split()
- except ValueError, e:
- raise ValueError("Wrong line format: %s." % repr(line))
- addr = fac.parse_addr_spec(addr_spec, defport=DEFAULT_RELAY_PORT, resolve=True)
- transports = parse_transport_chain(transport_spec)
- relays.setdefault(transports, [])
- relays[transports].append(addr)
- return relays
+ for line in fp.readlines():
+ try:
+ transport_spec, addr_spec = line.strip().split()
+ except ValueError, e:
+ raise ValueError("Wrong line format: %s." % repr(line))
+ addr = fac.parse_addr_spec(addr_spec, defport=DEFAULT_RELAY_PORT, resolve=True)
+ transport = Transport.parse(transport_spec)
+ af = addr_af(addr[0])
+ servers[af].addEndpoint(addr, transport)
def main():
opts, args = getopt.gnu_getopt(sys.argv[1:], "dhl:p:r:",
@@ -474,17 +518,12 @@ obfs2|websocket 1.4.6.1:4123\
"""
sys.exit(1)
- RELAYS.update(parse_relay_file(options.relay_filename))
-
- if not RELAYS:
- print >> sys.stderr, u"Warning: no relays configured."
-
- # Create RegSets for our supported transports
- for transport in RELAYS.keys():
- outermost_transport = get_outermost_transport(transport)
- if outermost_transport not in REGSETS_IPV4:
- REGSETS_IPV4[outermost_transport] = RegSet()
- REGSETS_IPV6[outermost_transport] = RegSet()
+ try:
+ with open(options.relay_filename) as fp:
+ parse_relay_file(SERVERS, fp)
+ except ValueError as e:
+ print >> sys.stderr, u"Could not parse file '%s': %s" % (repr(a), str(e))
+ sys.exit(1)
# Setup log file
if options.log_filename:
@@ -499,7 +538,8 @@ obfs2|websocket 1.4.6.1:4123\
server = Server(addrinfo[4], Handler)
log(u"start on %s" % fac.format_addr(addrinfo[4]))
- log(u"using relays %s" % str(RELAYS))
+ log(u"using IPv4 relays %s" % str(SERVERS[socket.AF_INET]._endpoints))
+ log(u"using IPv6 relays %s" % str(SERVERS[socket.AF_INET6]._endpoints))
if options.daemonize:
log(u"daemonizing")
diff --git a/facilitator/facilitator-test b/facilitator/facilitator-test
index e39cecd..b81b84e 100755
--- a/facilitator/facilitator-test
+++ b/facilitator/facilitator-test
@@ -7,7 +7,7 @@ import tempfile
import time
import unittest
-from facilitator import parse_transport_chain
+from facilitator import Transport, Reg, Endpoints, parse_relay_file
import fac
FACILITATOR_HOST = "127.0.0.1"
@@ -23,11 +23,169 @@ def gimme_socket(host, port):
s.connect(addrinfo[4])
return s
+class EndpointsTest(unittest.TestCase):
+
+ def setUp(self):
+ self.pts = Endpoints(af=socket.AF_INET)
+
+ def _observeProxySupporting(self, *supported_suf):
+ # semantically observe the existence of a proxy, to make our intent
+ # a bit clearer than simply calling findPrefixesForSuffixes
+ self.pts._findPrefixesForSuffixes(*supported_suf)
+
+ def test_addEndpoints_twice(self):
+ self.pts.addEndpoint("A", "a|b|p")
+ self.assertFalse(self.pts.addEndpoint("A", "zzz"))
+ self.assertEquals(self.pts._endpoints["A"], Transport("a|b", "p"))
+
+ def test_addEndpoints_lazy_indexing(self):
+ self.pts.addEndpoint("A", "a|b|p")
+ default_index = {"websocket": set()} # we always index known_suffixes
+
+ # no index until we've asked for it
+ self.assertEquals(self.pts._indexes, default_index)
+ self._observeProxySupporting("p")
+ self.assertEquals(self.pts._indexes["p"], set("A"))
+
+ # indexes are updated correctly after observing new addresses
+ self.pts.addEndpoint("B", "c|p")
+ self.assertEquals(self.pts._indexes["p"], set("AB"))
+
+ # indexes are updated correctly after observing new proxies
+ self.pts.addEndpoint("C", "a|q")
+ self._observeProxySupporting("q")
+ self.assertEquals(self.pts._indexes["q"], set("C"))
+
+ def test_supports_default(self):
+ # we know there are websocket-capable proxies out there;
+ # support them implicitly without needing to see a proxy.
+ self.pts.addEndpoint("A", "obfs3|websocket")
+ self.assertTrue(self.pts.supports("obfs3|websocket"))
+ self.assertFalse(self.pts.supports("xxx|websocket"))
+ self.assertFalse(self.pts.supports("websocket"))
+ self.assertFalse(self.pts.supports("unknownwhat"))
+ # doesn't matter what the first part is
+ self.pts.addEndpoint("B", "xxx|websocket")
+ self.assertTrue(self.pts.supports("xxx|websocket"))
+
+ def test_supports_seen_proxy(self):
+ # OTOH if some 3rd-party proxy decides to implement its own transport
+ # we are fully capable of supporting them too, but only if we have
+ # an endpoint that also speaks it.
+ self.assertFalse(self.pts.supports("obfs3|unknownwhat"))
+ suf = self._observeProxySupporting("unknownwhat")
+ self.assertFalse(self.pts.supports("obfs3|unknownwhat"))
+ self.pts.addEndpoint("A", "obfs3|unknownwhat")
+ self.assertTrue(self.pts.supports("obfs3|unknownwhat"))
+ self.assertFalse(self.pts.supports("obfs2|unknownwhat"))
+
+ def _test_serveReg_maxserve_infinite_roundrobin(self):
+ # case for servers, they never exhaust
+ self.pts.addEndpoint("A", "a|p")
+ self.pts.addEndpoint("B", "a|p")
+ self.pts.addEndpoint("C", "a|p")
+ for i in xrange(64): # 64 is infinite ;)
+ served = set()
+ served.add(self.pts._serveReg("ABC"))
+ served.add(self.pts._serveReg("ABC"))
+ served.add(self.pts._serveReg("ABC"))
+ self.assertEquals(served, set("ABC"))
+
+ def _test_serveReg_maxserve_finite_exhaustion(self):
+ # case for clients, we don't want to keep serving them
+ self.pts = Endpoints(af=socket.AF_INET, maxserve=5)
+ self.pts.addEndpoint("A", "a|p")
+ self.pts.addEndpoint("B", "a|p")
+ self.pts.addEndpoint("C", "a|p")
+ # test getNumUnservedEndpoints whilst we're at it
+ self.assertEquals(self.pts.getNumUnservedEndpoints(), 3)
+ for i in xrange(5):
+ served = set()
+ served.add(self.pts._serveReg("ABC"))
+ served.add(self.pts._serveReg("ABC"))
+ served.add(self.pts._serveReg("ABC"))
+ self.assertEquals(served, set("ABC"))
+ self.assertEquals(None, self.pts._serveReg("ABC"))
+ self.assertEquals(self.pts.getNumUnservedEndpoints(), 0)
+
+ def test_match_normal(self):
+ self.pts.addEndpoint("A", "a|p")
+ self.pts2 = Endpoints(af=socket.AF_INET)
+ self.pts2.addEndpoint("B", "a|p")
+ self.pts2.addEndpoint("C", "b|p")
+ self.pts2.addEndpoint("D", "a|q")
+ expected = (Reg("A", Transport("a","p")), Reg("B", Transport("a","p")))
+ empty = Endpoints.EMPTY_MATCH
+ self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p"]))
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
+
+ def test_match_unequal_client_server(self):
+ self.pts.addEndpoint("A", "a|p")
+ self.pts2 = Endpoints(af=socket.AF_INET)
+ self.pts2.addEndpoint("B", "a|q")
+ expected = (Reg("A", Transport("a","p")), Reg("B", Transport("a","q")))
+ empty = Endpoints.EMPTY_MATCH
+ self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p", "q"]))
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["p"]))
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["q"]))
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
+
+ def test_match_raw_server(self):
+ self.pts.addEndpoint("A", "p")
+ self.pts2 = Endpoints(af=socket.AF_INET)
+ self.pts2.addEndpoint("B", "p")
+ expected = (Reg("A", Transport("","p")), Reg("B", Transport("","p")))
+ empty = Endpoints.EMPTY_MATCH
+ self.assertEquals(expected, Endpoints.match(self.pts, self.pts2, ["p"]))
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
+
+ def test_match_many_prefixes(self):
+ self.pts.addEndpoint("A", "a|p")
+ self.pts.addEndpoint("B", "b|p")
+ self.pts.addEndpoint("C", "p")
+ self.pts2 = Endpoints(af=socket.AF_INET)
+ self.pts2.addEndpoint("D", "a|p")
+ self.pts2.addEndpoint("E", "b|p")
+ self.pts2.addEndpoint("F", "p")
+ # this test ensures we have a sane policy for selecting between prefix pools
+ expected = set()
+ expected.add((Reg("A", Transport("a","p")), Reg("D", Transport("a","p"))))
+ expected.add((Reg("B", Transport("b","p")), Reg("E", Transport("b","p"))))
+ expected.add((Reg("C", Transport("","p")), Reg("F", Transport("","p"))))
+ result = set()
+ result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
+ result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
+ result.add(Endpoints.match(self.pts, self.pts2, ["p"]))
+ empty = Endpoints.EMPTY_MATCH
+ self.assertEquals(expected, result)
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
+ self.assertEquals(empty, Endpoints.match(self.pts, self.pts2, ["x"]))
+
class FacilitatorTest(unittest.TestCase):
- def test_parse_transport_chain(self):
- self.assertEquals(parse_transport_chain("a"), ("a",))
- self.assertEquals(parse_transport_chain("a|b|c"), ("a","b","c"))
+ def test_transport_parse(self):
+ self.assertEquals(Transport.parse("a"), Transport("", "a"))
+ self.assertEquals(Transport.parse("|a"), Transport("", "a"))
+ self.assertEquals(Transport.parse("a|b|c"), Transport("a|b","c"))
+ self.assertEquals(Transport.parse(Transport("a|b","c")), Transport("a|b","c"))
+ self.assertRaises(ValueError, Transport, "", "")
+ self.assertRaises(ValueError, Transport, "a", "")
+ self.assertRaises(ValueError, Transport.parse, "")
+ self.assertRaises(ValueError, Transport.parse, "|")
+ self.assertRaises(ValueError, Transport.parse, "a|")
+ self.assertRaises(ValueError, Transport.parse, ["a"])
+ self.assertRaises(ValueError, Transport.parse, [Transport("a", "b")])
+
+ def test_parse_relay_file(self):
+ fp = StringIO()
+ fp.write("websocket 0.0.1.0:1\n")
+ fp.flush()
+ fp.seek(0)
+ af = socket.AF_INET
+ servers = { af: Endpoints(af=af) }
+ parse_relay_file(servers, fp)
+ self.assertEquals(servers[af]._endpoints, {('0.0.1.0', 1): Transport('', 'websocket')})
class FacilitatorProcTest(unittest.TestCase):
IPV4_CLIENT_ADDR = ("1.1.1.1", 9000)
1
0