Skip to content

Instantly share code, notes, and snippets.

@ymnk
Created November 24, 2008 13:37
Show Gist options
  • Select an option

  • Save ymnk/28465 to your computer and use it in GitHub Desktop.

Select an option

Save ymnk/28465 to your computer and use it in GitHub Desktop.
import java.net.{InetAddress, Socket, InetSocketAddress}
import java.io._
import scala.collection.mutable._
import scala.io.Source
import scala.concurrent.ops.spawn
/**
* This program expects to be comipled with ARCFour.scala[1] .
* [1] http://gist.github.com/28462
*/
class WinnyARCFour extends ARCFour{
private def chop(key:Array[Byte])={
key.indexOf(0) match {
case -1 => key
case 0 => new Array[Byte](1)
case i => key.subArray(0, i)
}
}
override def init(key:Array[Byte]) = super.init(chop(key))
}
object WinnyNode{
private val key=" piewf6ascxlv".getBytes
private val ipport="([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+):([0-9]+)".r
/**
* The node has the following form, for example,
* @5e4d8e761fb09aaed8857e9397c93bae4617
* and it will be decoded to the pair of ip address and port.
*/
def decode(node:String):Option[(InetAddress, Int)] = key.synchronized{
val src = toArray(node.substring(1))
key(0) = src(0)
val dest = {
val cipher = new WinnyARCFour
cipher.init(key)
cipher.decrypt(src, 1, src.length-1)
}
if((checkSum(dest)&0xff) == (src(0)&0xff)){
new String(dest) match {
case ipport(ip, port) if !ip.startsWith("192.168.") =>
try{
Some((InetAddress.getByName(ip) -> Integer.parseInt(port)))
}
catch{ case e => None }
case _ => None
}
}
else None
}
private def toArray(hex:String):Array[Byte]={
var i=0
(new Array[Int](hex.length/2) /: hex){ (buf,b) => {
buf(i/2)<<=4
buf(i/2)|=(if('0'<=b&&b<='9') b-'0' else b-'a'+10)
i+=1
buf
}
}.map(i=>(i&0xff).asInstanceOf[Byte])
}
private def checkSum(buf:Array[Byte]) = (0 /: buf){(s,i)=>s+(i&0xff)}
}
object WinnyPool extends Iterator[(InetAddress, Int)]{
private val pool=new Stack[(InetAddress, Int)]
private val done=Set.empty[(InetAddress, Int)]
def += (node:String){
for(n <- WinnyNode.decode(node))
this+=(n._1, n._2)
}
def += (ip:InetAddress, port:Int)=synchronized{
if(!ip.toString.startsWith("/192.168") &&
!pool.contains((ip, port)) &&
!done.contains((ip, port))){
// println("WinnyPool += "+ip+":"+port)
pool+=(ip, port)
}
}
def -= (ip:InetAddress, port:Int) = synchronized{ done -= (ip -> port) }
def hasNext = synchronized{
if(!pool.isEmpty) true
else if(!done.isEmpty){ done.foreach{pool.push(_)}; done.clear; true}
else false
}
def next = synchronized{
if(!pool.isEmpty){
pool.pop match{
case p => done += p; p
}
}
else null
}
}
abstract class WinnyIO {
private val key=new Array[Byte](4)
private val cipher_in = new WinnyARCFour
private val mask=Array(0, 0x39, 0x39, 0x39).map(_.asInstanceOf[Byte])
private def socket(ip:InetAddress, port:Int):Option[Socket] = try{
Some(new Socket match {
case s => {
s.connect(new InetSocketAddress(ip, port), 3*1000);
s.setSoTimeout(10*1000)
s
}
}
)
}
catch {
case e=> {
WinnyPool -= (ip, port)
None
}
}
def swap(b:Array[Byte]) = b.foldRight(0){(b, result)=>(result<<8)+(b&0xff)}
implicit def toDataInputStream(i:InputStream) =
new DataInputStream(i){
def readBytes(n:Int)={
if((n & ~(0xffff))!=0)
throw new IOException("attack?")
val buf=new Array[Byte](n)
readFully(buf)
buf
}
}
def connect(ip:InetAddress, port:Int) = try {
for(socket<-socket(ip, port)){
val (in, out) = (socket.getInputStream, socket.getOutputStream)
try{
in.readChar
in.readFully(key)
cipher_in.init(key)
def nextData = {
val len = swap(cipher_in.decrypt(in.readBytes(4), 0, 4))
val d=cipher_in.decrypt(in.readBytes(len), 0, len)
if(d(0)==0){
cipher_in.init((key zip mask).map{
case (k,m) => ((k&0xff)^m).asInstanceOf[Byte]})
}
d
}
var loop=true
while(loop){
val data = nextData
commandMap.get(data(0)) match {
case Some(c) => c.proc(data)
case _ =>
}
}
}
finally{
in.close
out.close
}
}
}catch{case e => }
abstract class Command {
def toInputStream(buf:Array[Byte]) = new ByteArrayInputStream(buf)
def proc(buf:Array[Byte])
}
private val commandMap = Map.empty[Int, Command]
def addCommand(i:Int, c:Command) = commandMap.synchronized{
commandMap += (i -> c)
}
}
class FilesOnWinny_ extends WinnyIO {
object Command13 extends Command {
def proc(buf:Array[Byte]){
var buf_length = buf.length
val in = toInputStream(buf)
in.readByte; buf_length -= 1
val response_flag=in.readByte&0xff
val diff_flag=in.readByte&0xff
val down_flag=in.readByte&0xff
val bbs_flag=in.readByte&0xff
val query_id=in.readInt
val keyword_length=in.readByte&0xff
val keyword=in.readBytes(keyword_length);
buf_length -= (9+keyword_length)
val trip=in.readBytes(11); buf_length -= 11
//println("keyword: "+new String(keyword, "Windows-31J"))
val node_count=in.readByte&0xff; buf_length -= 1
val hosts=HashSet.empty[(InetAddress, Int)]
for(_ <- (0 until node_count)){
val h=InetAddress.getByAddress(in.readBytes(4))
val p=swap(in.readBytes(4))&0xffff
hosts += (h -> p) ; buf_length -= 8
WinnyPool += (h, p)
}
if(buf_length==0)
return
for(_ <- (0 until swap(in.readBytes(2)))){
val kh=InetAddress.getByAddress(in.readBytes(4))
val kp=swap(in.readBytes(2))
val bh=InetAddress.getByAddress(in.readBytes(4))
val bp=swap(in.readBytes(2))
val fs=swap(in.readBytes(4))
val fh=in.readBytes(16)
val fnl=in.readByte&0xff
val fnk1=in.readBytes(1)
val fnk2=in.readBytes(1)
var fn = new WinnyARCFour match {
case c => c.init(fnk1); c.decrypt(in.readBytes(fnl), 0, fnl)
}
val ft=in.readBytes(11)
val bal=in.readByte&0xff
val kt=in.readChar&0xffff
val rb=in.readInt
val ut=in.readInt
val igf=in.readByte&0xff
val kv=in.readByte&0xff
WinnyPool += (kh, kp)
println(new String(fn, "Windows-31J"))
}
}
}
addCommand(13, Command13)
}
/**
* The "main" method expects files or URIs, which includes winny nodes
* par each line.
*/
object FilesOnWinny {
def main(arg:Array[String])={
import Source.{fromFile, fromURL}
def toSource(f:String) =
if(f.startsWith("http://")) fromURL(f) else fromFile(f)
for(i<-arg.map(toSource(_));
j<-i.mkString("").split("\n").reverse if (j.length %2)==1)
WinnyPool += j
(0 to 3).foreach{ _ => spawn{
for(w <- WinnyPool){
new FilesOnWinny_().connect(w._1, w._2)
try{ Thread.sleep(1000) }catch{case e => }
}}}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment