New upstream version 0.0~git20190224.6dc102d
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 idk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
12
Makefile
Normal file
12
Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
apt-transport-i2phttp:
|
||||
go build \
|
||||
-a \
|
||||
-tags netgo \
|
||||
-ldflags '-w -extldflags "-static"'
|
||||
|
||||
install: apt-transport-i2phttp
|
||||
install -m755 apt-transport-i2phttp /usr/lib/apt/methods/i2p
|
||||
|
||||
clean:
|
||||
go clean
|
35
README.md
Normal file
35
README.md
Normal file
@ -0,0 +1,35 @@
|
||||
apt-transport-i2phttp, HTTP-based I2P Transport for apt
|
||||
=======================================================
|
||||
|
||||
This is a simple transport for downloading debian packages from a repository
|
||||
over i2p. It uses the built-in HTTP proxy or one you configure. It's a
|
||||
modified version of [diocles/apt-tranport-http-golang](https://github.com/diocles/apt-transport-http-golang),
|
||||
a plain HTTP Transport for apt.
|
||||
|
||||
Besides that, I think most would agree that it is simpler to use an apt
|
||||
transport to detect when a package should be retrieved from an i2p service.
|
||||
Especially in cases where the user is mixing packages from Tor, I2P, and
|
||||
Clearnet sources, this process can become confusing and involve configuring
|
||||
multiple applications along with apt. Instead, apt-transport-i2phttp works with
|
||||
other apt transports like apt-transport-tor and even apt-transport-i2p(A SAM
|
||||
based alternate i2p transport), requiring no configuration on the vast majority
|
||||
of systems.
|
||||
|
||||
To install it:
|
||||
--------------
|
||||
|
||||
As long as you have an i2p router installed the http proxy should be enabled
|
||||
by default. You can just:
|
||||
|
||||
make build && sudo make install
|
||||
|
||||
to install ./apt-transport-i2phttp to /usr/lib/apt/methods/i2p, requiring no
|
||||
additional configuration.
|
||||
|
||||
To use it:
|
||||
---------
|
||||
|
||||
To add an eepSite to your sources.list, for example(This example site is down,
|
||||
I'll have a new one for you to use shortly):
|
||||
|
||||
deb i2p://http://wnhxwrq4fkn3cov6bnqsdaniubeo3625rmsm53yaz336bxvtiqeq.b32.i2p/deb-pkg rolling main
|
248
main.go
Normal file
248
main.go
Normal file
@ -0,0 +1,248 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Status string
|
||||
StatusCode int
|
||||
Header Header
|
||||
Exit int
|
||||
}
|
||||
|
||||
type Header map[string][]string
|
||||
|
||||
func (h Header) Add(key, value string) {
|
||||
h[key] = append(h[key], value)
|
||||
}
|
||||
|
||||
func (h Header) Get(key string) string {
|
||||
if value, ok := h[key]; ok {
|
||||
if len(value) > 0 {
|
||||
return value[0]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) String() string {
|
||||
s := []string{m.Status}
|
||||
for k, values := range m.Header {
|
||||
for _, v := range values {
|
||||
s = append(s, k+": "+v)
|
||||
}
|
||||
}
|
||||
return strings.Join(s, "\n") + "\n\n"
|
||||
}
|
||||
|
||||
func ReadInConfig() {
|
||||
if os.Getenv("APT_TRANSPORT_I2PHTTP_CONF") == "" {
|
||||
os.Setenv("APT_TRANSPORT_I2PHTTP_CONF", "/etc/apt-transport-i2phttp/i2p.conf")
|
||||
} else {
|
||||
os.Setenv("APT_TRANSPORT_I2PHTTP_CONF", os.Getenv("APT_TRANSPORT_I2PHTTP_CONF"))
|
||||
}
|
||||
if _, err := os.Stat(os.Getenv("APT_TRANSPORT_I2PHTTP_CONF")); os.IsNotExist(err) {
|
||||
os.Setenv("I2P_HTTP_PROXY", "http://127.0.0.1:4444")
|
||||
os.Setenv("HTTP_PROXY", "http://127.0.0.1:4444")
|
||||
} else if err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
bytes, err := ioutil.ReadFile(os.Getenv("APT_TRANSPORT_I2PHTTP_CONF"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
host := "127.0.0.1"
|
||||
port := "4444"
|
||||
for _, val := range strings.Split(string(bytes), "\n") {
|
||||
v := strings.Split(val, "=")
|
||||
if len(v) == 2 {
|
||||
if v[0] == "host" {
|
||||
host = v[0]
|
||||
}
|
||||
if v[0] == "port" {
|
||||
port = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
os.Setenv("I2P_HTTP_PROXY", "http://"+host+":"+port)
|
||||
os.Setenv("HTTP_PROXY", "http://"+host+":"+port)
|
||||
}
|
||||
if os.Getenv("I2P_HTTP_PROXY") == "" {
|
||||
os.Setenv("I2P_HTTP_PROXY", "http://127.0.0.1:4444")
|
||||
os.Setenv("HTTP_PROXY", "http://127.0.0.1:4444")
|
||||
} else {
|
||||
os.Setenv("HTTP_PROXY", os.Getenv("I2P_HTTP_PROXY"))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
ReadInConfig()
|
||||
c := make(chan *Message)
|
||||
go output(c)
|
||||
sendCapabilities(c)
|
||||
|
||||
stdin := bufio.NewScanner(os.Stdin)
|
||||
for stdin.Scan() {
|
||||
line := stdin.Text()
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
s := strings.SplitN(line, " ", 2)
|
||||
code, err := strconv.Atoi(s[0])
|
||||
if err != nil {
|
||||
fmt.Println("Malformed message!")
|
||||
os.Exit(100)
|
||||
}
|
||||
request := &Message{
|
||||
Status: line,
|
||||
StatusCode: code,
|
||||
Header: Header{},
|
||||
}
|
||||
|
||||
for stdin.Scan() {
|
||||
line := stdin.Text()
|
||||
|
||||
if line == "" {
|
||||
process(c, request)
|
||||
break
|
||||
}
|
||||
s := strings.SplitN(line, ": ", 2)
|
||||
request.Header.Add(s[0], s[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func output(c <-chan *Message) {
|
||||
for {
|
||||
m := <-c
|
||||
os.Stdout.Write([]byte(m.String()))
|
||||
if m.Exit != 0 {
|
||||
os.Exit(m.Exit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendCapabilities(c chan<- *Message) {
|
||||
caps := &Message{
|
||||
Status: "100 Capabilities",
|
||||
Header: Header{},
|
||||
}
|
||||
|
||||
caps.Header.Add("Version", "1.2")
|
||||
caps.Header.Add("Pipeline", "true")
|
||||
caps.Header.Add("Send-Config", "true")
|
||||
|
||||
c <- caps
|
||||
}
|
||||
|
||||
func process(c chan<- *Message, m *Message) {
|
||||
switch m.StatusCode {
|
||||
case 600:
|
||||
go fetch(c, m)
|
||||
case 601:
|
||||
// TODO: parse config?
|
||||
default:
|
||||
fail := &Message{
|
||||
Status: "401 General Failure",
|
||||
Header: Header{},
|
||||
Exit: 100,
|
||||
}
|
||||
fail.Header.Add("Message", "Status code not implemented")
|
||||
|
||||
c <- fail
|
||||
}
|
||||
}
|
||||
|
||||
func fetch(c chan<- *Message, m *Message) {
|
||||
uri := m.Header.Get("URI")
|
||||
filename := m.Header.Get("Filename")
|
||||
|
||||
// TODO: If-Modified-Since
|
||||
|
||||
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
c <- &Message{
|
||||
Status: "400 URI Failure",
|
||||
Header: Header{
|
||||
"Message": []string{"Could not open file: " + err.Error()},
|
||||
"URI": []string{uri},
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// TODO: Fix bug with appending to existing files
|
||||
// TODO: implement range requests if file already exists
|
||||
|
||||
realURI := strings.TrimPrefix(uri, "i2p://")
|
||||
|
||||
resp, err := http.Get(realURI)
|
||||
if err != nil {
|
||||
c <- &Message{
|
||||
Status: "400 URI Failure",
|
||||
Header: Header{
|
||||
"Message": []string{"Could not fetch URI: " + err.Error()},
|
||||
"URI": []string{uri},
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
started := &Message{
|
||||
Status: "200 URI Start",
|
||||
Header: Header{
|
||||
"URI": []string{uri},
|
||||
},
|
||||
}
|
||||
// TODO: add Last-Modified header
|
||||
|
||||
c <- started
|
||||
|
||||
md5Hash := md5.New()
|
||||
sha1Hash := sha1.New()
|
||||
sha256Hash := sha256.New()
|
||||
sha512Hash := sha512.New()
|
||||
|
||||
if _, err = io.Copy(io.MultiWriter(file, md5Hash, sha1Hash, sha256Hash, sha512Hash), resp.Body); err != nil {
|
||||
c <- &Message{
|
||||
Status: "400 URI Failure",
|
||||
Header: Header{
|
||||
"Message": []string{"Could not write file: " + err.Error()},
|
||||
"URI": []string{uri},
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
success := &Message{
|
||||
Status: "201 URI Done",
|
||||
Header: Header{},
|
||||
}
|
||||
success.Header.Add("URI", uri)
|
||||
success.Header.Add("Filename", filename)
|
||||
// TODO Size, Last-Modified
|
||||
md5Hex := hex.EncodeToString(md5Hash.Sum(nil)[:])
|
||||
success.Header.Add("MD5-Hash", md5Hex)
|
||||
success.Header.Add("MD5Sum-Hash", md5Hex)
|
||||
success.Header.Add("SHA1-Hash", hex.EncodeToString(sha1Hash.Sum(nil)[:]))
|
||||
success.Header.Add("SHA256-Hash", hex.EncodeToString(sha256Hash.Sum(nil)[:]))
|
||||
success.Header.Add("SHA512-Hash", hex.EncodeToString(sha512Hash.Sum(nil)[:]))
|
||||
|
||||
c <- success
|
||||
}
|
Reference in New Issue
Block a user