2010年1月8日星期五

Windows 7 IPv6不完全折腾

按照网上很多人的说法,Windows 7下只需要开一个有管理员权限的控制台,执行类似如下两条命令即可:

netsh interface ipv6 isatap set router isatap.tsinghua.edu.cn
netsh interface ipv6 isatap set state enabled

然而很奇怪的是,ipconfig显示我已经有了一个2001开头的IPv6地址,ipv6.google.com却打不开。试了很多个isatap服务器都不行,正像只没头苍蝇一样在网上乱逛的时候,鬼使神差地敲了一句ping -6 ipv6.google.com。呃,居然有回复?!然后试着在浏览器里访问http://[2001:4860:c004::68],还真的可以打开……

C:\>nslookup www.kame.net
服务器:  google-public-dns-a.google.com
Address:  8.8.8.8

非权威应答:
名称:    www.kame.net
Addresses:  2001:200:0:8002:203:47ff:fea5:3085
          203.178.141.194


C:\>ping www.kame.net

正在 Ping www.kame.net [203.178.141.194] 具有 32 字节的数据:
来自 203.178.141.194 的回复: 字节=32 时间=111ms TTL=45
来自 203.178.141.194 的回复: 字节=32 时间=131ms TTL=45
来自 203.178.141.194 的回复: 字节=32 时间=99ms TTL=45
来自 203.178.141.194 的回复: 字节=32 时间=137ms TTL=45

203.178.141.194 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 99ms,最长 = 137ms,平均 = 119ms

C:\>nslookup ipv6.google.com
服务器:  google-public-dns-a.google.com
Address:  8.8.8.8

非权威应答:
名称:    ipv6.l.google.com
Address:  2001:4860:c004::68
Aliases:  ipv6.google.com


C:\>ping ipv6.google.com
Ping 请求找不到主机 ipv6.google.com。请检查该名称,然后重试。

C:\>ping -6 ipv6.google.com

正在 Ping ipv6.l.google.com [2001:4860:c004::68] 具有 32 字节的数据:
来自 2001:4860:c004::68 的回复: 时间=401ms
来自 2001:4860:c004::68 的回复: 时间=400ms
来自 2001:4860:c004::68 的回复: 时间=401ms
来自 2001:4860:c004::68 的回复: 时间=401ms

2001:4860:c004::68 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 400ms,最长 = 401ms,平均 = 400ms

nslookup能够获得IPv6地址,而且排在IPv4地址的前面,似乎DNS并没有什么问题。然而无论是ping还是浏览器,却都对IPv6地址视若无睹,就好像IPv6根本没有启用一样。

接下来抱着试一试的心情,我又往hosts文件里添加了几个IPv6地址,比如:2001:4860:c004::68 www.google.com

居然,好了?嗯,ping没问题了,浏览器也能正常打开。看起来还是DNS解析的问题,网络应用程序默认情况下无法取得IPv6地址。

可问题究竟出在哪?在网上翻了很久,仍然不明所以。我甚至开始怀疑,是不是自己网络环境的原因(我用没有独立IP的小区宽带,家里还有一个路由器,相当于是内网中的内网),但试着在Ubuntu里装上miredo,同样用Google DNS,同一个ISATAP服务器,一切正常。

看来,只能是Windows 7的设置有问题了。虽然解决办法到现在也没找到,不过至少hosts可用,先将就吧……

用上IPv6之后,我常用的绝大部分网络程序都没出什么问题,只有Privoxy。跑到官网上看了看,最新版的3.0.15是可以支持IPv6的,只是mingw32编译的Windows版还不行,需要修改代码。Socket编程我碰都没碰过,这可不是我擅长的领域,所以试着用Cygwin编译了一个Privoxy,能用。虽然没有GUI,不过那个窗口也没大用,继续将就……

2010年1月4日星期一

初尝黑苹果

最近手上多出来一块160G的硬盘,拿来干什么都太小,突然想起来还有黑苹果这种东西,正好对传说中Windows的GUI+Linux的Console很有兴趣,于是装上试了试。

手上没有刻录机,安装过程参考了远景论坛的这张帖子,基本没有遇到太大的问题,不过一些小地方还是耗费了不少精神。

Boot Think 2.3.18在OS X中安装至MBR无法引导,Chameleon 2.0 RC4虽然可以安装,但是有部分KEXT又会无法加载。最后索性多分了一个区装上Windows XP,把Boot Think放在Windows分区上。反正现在用的Windows 7也有部分软件表现不良,就当备用吧。

驱动方面方面,我用了如下这些KEXT:AppleNForceATA、ElliottForceLegacyRTC、fakesmc、NullCPUPowerManagement、OpenHaltRestart、PlatformUUID、VoodooHDA。

DSDT.aml虽然可以解决很多问题,可是需要针对不同主板不同BIOS版本进行修改,而网上的文档很杂乱,那个ACPI Patcher又很不好用,再加上找到ElliottForceLegacyRTC这个KEXT解决了BIOS Checksum Error的问题,所以我还是放弃了。

显卡驱动一开始用的是NVEnabler,可是总觉得中文小字体的表现很糟糕,颜色很淡又很模糊。找了一个EFI Studio生成显卡的EFI String,写入Boot Think的Darwin目录下的com.apple.Boot.plist(如果是com.apple.Boot__.plist,则改名之),感觉似乎稍好了些。

Update:关于中文字体颜色太淡的问题我已经放弃了。喜欢折腾的人去看看这个文件:/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist,这是设置默认字体的地方,跟Linux的fontconfig差不多。另外还有:defaults -currentHost write -globalDomain AppleFontSmoothing -int 1,取值1、2、3分别代表渲染度由淡至浓。

AMD的CPU还需要用Marvin's AMD Utility生成CPUID的补丁,引导至单用户模式(参数-s)打上补丁才能用iTunes和QuickTime。点菜单栏上的“关于本机”强制注销的问题,装上AppleSMBIOS和AppleSMBIOSEFI两个KEXT可以解决。

XCode 3.1.4在10.6下貌似有问题,装完以后/usr/bin下并没有gcc等东西,重下一个XCode 3.2之后问题解决。不过System Tools在AMD CPU的机器上不能装,会五国。(装上了也没关系,-x引导安全模式,运行/Developer/Applications/Performance Tools/CHUD下的CHUD Remover即可。)

安装部分大致就是这样,虽然启动有些慢(怀疑是AppleNForceATA的问题,可找不到可以替换的KEXT),不过已经完全可用了。感觉黑苹果的破解程度还是颇高,即使当成日常使用的系统,也完全没有问题。

至于使用感受,下次有精神再说吧……

Update:关于Boot Think无法引导Grub2(Ubuntu等)的问题,用任意一个十六进制编辑器打开Darwin/rc/grubloader2,搜索“menu.lst”,全部替换为“grub.cfg”即可。

2009年11月19日星期四

eD2k Hash in Ruby

很久以来一直都以为eD2k Hash就是MD4(罪魁祸首),这几天才发现错得很离谱。

花了点时间查资料,写了个用以生成eD2k链接的模块,包括AICH部分。代码有个偷懒的地方,读取文件的缓存大小必须为PART_SIZE,如果要拿去用的话请留意。Ruby 1.8.6和1.8.7的File.size在Windows平台下有bug,当文件尺寸比较大时会返回负数,这一点也请留意。

部分代码移植自pyaich和Python的Base64模块,GPL许可证。

require 'openssl'

module OtNtH
  module Digest
    # fit for eD2k Hash
    PART_SIZE = 9_728_000
    BLOCK_SIZE = 184_320

    class ED2K
      def initialize
        @h = ''
        @l = 0
      end
      
      def update(part)
        @h += OpenSSL::Digest::MD4.new(part).digest
        @l = part.size
      end
      
      def hexdigest
        @h += OpenSSL::Digest::MD4.new('').digest if @l == PART_SIZE
        OpenSSL::Digest::MD4.new(@h).hexdigest
      end
      
      def name
        'ED2K'
      end
    end # class ED2K

    class AICH
      class HashTree
        attr_accessor :dataSize, :isLeft, :baseSize, :hash, :leftTree, :rightTree
        
        def initialize(dataSize, isLeftBranch, baseSize)
          @dataSize = dataSize
          @isLeft = isLeftBranch
          @baseSize = baseSize
          @hash = nil
          @leftTree = nil
          @rightTree = nil
        end
        
        def loadLowestLevelHashes(hashList)
          if @dataSize <= @baseSize then
            @hash = hashList.shift
            return true
          else
            extra = @dataSize % @baseSize != 0 ? 1 : 0
            numBlocks = @dataSize / @baseSize + extra
            extra = isLeft ? 1 : 0
            sizeLeft = ((numBlocks + extra)/2) * @baseSize
            sizeRight = @dataSize - sizeLeft
            leftBaseSize = sizeLeft <= PART_SIZE ? BLOCK_SIZE : PART_SIZE
            @leftTree = HashTree.new(sizeLeft, true, leftBaseSize)
            
            rightBaseSize = sizeRight <= PART_SIZE ? BLOCK_SIZE : PART_SIZE
            @rightTree = HashTree.new(sizeRight, false, rightBaseSize)
            return @leftTree.loadLowestLevelHashes(hashList) && @rightTree.loadLowestLevelHashes(hashList)
          end
        end
        
        def reCalculateHash
          if @leftTree and @rightTree then
            return false if (not @leftTree.reCalculateHash) or (not @rightTree.reCalculateHash)
            h = OpenSSL::Digest::SHA1.new
            h.update(@leftTree.hash)
            h.update(@rightTree.hash)
            @hash = h.digest
            return true
          else
            return true
          end
        end
      end
      
      def initialize
        @hash_list = []
        @data_size = 0
      end
      
      def update(part)
        psize = part.size
        @data_size += psize
        pos = 0
        while pos < psize
          @hash_list.push OpenSSL::Digest::SHA1.new(part[pos, BLOCK_SIZE]).digest
          pos += BLOCK_SIZE
        end
      end
      
      def hexdigest
        blockSize = @data_size <= PART_SIZE ? BLOCK_SIZE : PART_SIZE
        tree = HashTree.new(@data_size, true, blockSize)
        tree.loadLowestLevelHashes(@hash_list)
        tree.reCalculateHash
        return Base32.encode32(tree.hash)
      end
      
      def name
        'ED2K'
      end
    end # class AICH
  end # module Digest
  
  module Base32
    B32TAB = 'abcdefghijklmnopqrstuvwxyz23456789'
    module_function
    
    def encode32(str)
      parts = []
      quanta, leftover = str.size.divmod(5)
      if leftover != 0 then
        str += ("\0" * (5 - leftover))
        quanta += 1
      end
      quanta.times do |i|
        c1, c2, c3 = str[i*5..(i+1)*5].unpack('nnC')
        c2 += (c1 & 1) << 16
        c3 += (c2 & 3) << 8
        parts.push(
          B32TAB[c1 >> 11],
          B32TAB[(c1 >> 6) & 0x1f],
          B32TAB[(c1 >> 1) & 0x1f],
          B32TAB[c2 >> 12],
          B32TAB[(c2 >> 7) & 0x1f],
          B32TAB[(c2 >> 2) & 0x1f],
          B32TAB[c3 >> 5],
          B32TAB[c3 & 0x1f]
        )
      end
      encoded = parts.join('')
      case leftover
      when 1
        encoded[0..-6] + '====='
      when 2
        encoded[0..-4] + '===='
      when 3
        encoded[0..-3] + '==='
      when 4
        encoded[0..-1] + '='
      else
        encoded
      end
    end
  end # module Base32
end # module OtNtH

if __FILE__ == $0 then
  exit if not File.exists?(ARGV[0])
  a = OtNtH::Digest::AICH.new
  e = OtNtH::Digest::ED2K.new
  File.open(ARGV[0], 'rb') do |f|
    buf = ''
    while f.read(OtNtH::Digest::PART_SIZE, buf)
      a.update(buf)
      e.update(buf)
    end
  end
  puts "ed2k://|file|#{File.basename(ARGV[0])}|#{File.size(ARGV[0])}|#{e.hexdigest}|h=#{a.hexdigest}|/"
end