<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"
	"database/sql"
	"strings"
	_ "github.com/go-sql-driver/mysql"
	"flag"
)

type User struct {
	username string
}

type GithubUser struct {
	Id int
	Login string
	Url string
}

// db thing.
type GithubRepo struct {
	pk int
	User string // should probably be Login
	RepoName string // should probably be Name
}

func (g *GithubRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.User, g.RepoName)
}

type GithubApiRepo struct {
	Id int
	Owner GithubUser
	Name NString
	Description NString
}

func (g *GithubApiRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.Owner.Login, g.Name)
}

type Activity struct {
	Id int
	github_id int
	activity_type string // should be enum
	created_at time.Time
	Username string
	repo GithubRepo // this isn't going to be memory efficient
	Meta string // full payload of json object
}

type Treeish struct {
	Label string
	Sha string
	Repo GithubApiRepo
	Html_url string
	User GithubUser
}

type Commit struct {
	Sha string
	Message string
	Author struct {
		Name string
		Email string
	}
}

func (c *Commit) ShortSha() string {
	return c.Sha[0:6]
}

func (c *Commit) ShortCommit() string {
	msg := strings.Split(c.Message, "\n")[0]
	if len(msg) > 80 {
		return msg[0:77] + "..."
	}
	return msg
}

// Nullable String
type NString string

func (n *NString) UnmarshalJSON(b []byte) (err error) {
	if string(b) == "null" {
		return nil
	}
	return json.Unmarshal(b, (*string)(n))
}

// get user's followed users
// get user's followed repos
// for each ^, pull out activity (goroutine)
//   return rendered template bits
// join template bits

// Does not take over ownership of db.
func get_users_repos(db *sql.DB, user_id int) ([]GithubRepo, error) {
	var repo_count int

	// TODO(justinabrahms): Should really alter the schema such
	// that the join from user <-> repo doesn't go through
	// userprofiles.
	row := db.QueryRow("SELECT count(*) FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)

	err := row.Scan(&repo_count)
	if (err != nil) {
		return nil, err
	}
	var repos = make([]GithubRepo, repo_count)

	rows, err := db.Query("SELECT r.id, username, project_name FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)
	// what's a good way to handle this not working?
	if (err != nil) {
		return nil, err
	}
	defer rows.Close()

	var username, project_name string
	var pk int
	for rows.Next() {
		err = rows.Scan(&pk, &username, &project_name)
		if (err != nil) {
			fmt.Println("Error!: ", err)
		}
		var repo = GithubRepo{pk, username, project_name}
		repos = append(repos, repo)
	}

	return repos, err
}

func get_repo_activity(db *sql.DB, repo *GithubRepo) (activity_list []Activity, err error){
	rows, err := db.Query(
		"SELECT a.id, a.event_id, a.type, a.created_at, ghu.name, r.username, r.project_name, meta" +
		" FROM streamer_activity a" +
		" JOIN streamer_repo r on r.id=a.repo_id" +
		" JOIN streamer_githubuser ghu on ghu.id=a.user_id" +
		" WHERE repo_id = ?", repo.pk)
	if (err != nil) {
		return nil, err
	}
	defer rows.Close()

	activity_list = make([]Activity, 0)

	var (
		pk, github_id, repo_id int
		activity_type, username, repo_project, repo_user, meta, created_str string
		created_at time.Time
	)

	for rows.Next() {
		err = rows.Scan(&pk, &github_id, &activity_type, &created_str, &username, &repo_user, &repo_project, &meta)
		if (err != nil) {
			fmt.Println("ERR: ", err)
		}

		created_at, err = time.Parse("2006-01-02 15:04:05", created_str)
		if (err != nil) {
			fmt.Println("Can't parse the format of ", created_str)
			return nil, err
		}

		activity_list = append(activity_list,
			Activity{pk, github_id, activity_type, created_at, username,
			GithubRepo{repo_id, repo_user, repo_project}, meta})
	}

	return
}

func commit_comment_render(activities []Activity, long_template bool) string { return "" }
func pull_request_comment_render(activities []Activity, long_template bool) string { return "" }
func member_render(activities []Activity, long_template bool) string { return "" }
func download_render(activities []Activity, long_template bool) string { return "" }
func fork_apply_render(activities []Activity, long_template bool) string { return "" }
func team_add_render(activities []Activity, long_template bool) string { return "" }
func gist_render(activities []Activity, long_template bool) string { return "" }
func follow_render(activities []Activity, long_template bool) string { return "" }

func repo_to_template(repo GithubRepo, activities []Activity, render_map map[string]func([]Activity, bool)string) string {
	var activity_map = make(map[string][]Activity)
	for _, activity := range activities {
		// This seems like a lot of juggling. Is there a better way?
		arr := activity_map[activity.activity_type]
		if (arr == nil) {
			arr = make([]Activity, 0)
			activity_map[activity.activity_type] = arr
		}
		arr = append(arr, activity)
		activity_map[activity.activity_type] = arr
	}

	// activity_map: activity_type => []activity
	var response = fmt.Sprintf("%s:\n", repo.FullName())
	for activity_type, activities := range activity_map {
		fn, ok := render_map[activity_type]
		if ok {
			response += fn(activities, true)
		} else {
			log.Print("Not sure how to render activites of type ", activity_type)
		}
	}
	return response
}

func repo_to_string(db *sql.DB, repo GithubRepo, response chan string) {
	activities, err := get_repo_activity(db, &repo)
	if (err != nil) {
		fmt.Println("ERR: ", err)
		os.Exit(1)
	}

	activity_type_to_renderer := map[string]func([]Activity, bool)string {
		"P": push_render,
		"PR": pull_request_render,
		"D": delete_render,
		"C": create_render,
		"W": watch_render,
		"F": fork_render,
		"IC": issue_comment_render,
		"Gl": wiki_render, // Gl is for Gollum, Github's wiki thing.
		"I": issue_render,
		"Pb": public_render,
		// TODO: TA (team add), FA (fork apply),
		// TODO: DO (Download), RC (Pull Request Review Comment),
		// TODO: Fl (Follow), G (Gist)
	}

	response <- repo_to_template(repo, activities, activity_type_to_renderer)
}

func mark_user_repo_sent(user User, repo GithubRepo) {
	// should find the streamer_userprofile_repo row for the repo
	// / user combo, mark its last_sent as now
}

var user_id = flag.Int("user_id", 1, "ID of user to output.")

// TODO: Github Users
// TODO: Email people
// TODO: Limit information by time (past day or w/e)

func main() {
	flag.Parse()

	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/%s?charset=utf8", os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_DB")))
	if (err != nil) {
		log.Fatalf("Unable to connect to mysql. ", err)
	}
	defer db.Close()

	repos, err := get_users_repos(db, *user_id)
	if err != nil {
		log.Fatalf("Error fetching user's repos. %s", err)
	}

	fmt.Println("Got ", len(repos), " repos.")

	response_chan := make(chan string, len(repos))
	for _, repo := range repos {
		repo_to_string(db, repo, response_chan)
	}

	var response string
	for num_responses := 0; num_responses < len(repos); {
		select {
		case r := <-response_chan:
			num_responses++
			response += r
		}
	}

	mark_user_repo_sent(repo, user)

	fmt.Println(response)
	return
}
<commit_msg>Update script to send activities that the user hasn't gotten before.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"
	"database/sql"
	"strings"
	_ "github.com/go-sql-driver/mysql"
	"flag"
	"strconv"
)

type User struct {
	Id int
	username string
}

type GithubUser struct {
	Id int
	Login string
	Url string
}

// db thing.
type GithubRepo struct {
	Id int
	User string // should probably be Login
	RepoName string // should probably be Name
}

func (g *GithubRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.User, g.RepoName)
}

type GithubApiRepo struct {
	Id int
	Owner GithubUser
	Name NString
	Description NString
}

func (g *GithubApiRepo) FullName() string {
	return fmt.Sprintf("%s/%s", g.Owner.Login, g.Name)
}

type Activity struct {
	Id int
	github_id int
	activity_type string // should be enum
	created_at time.Time
	Username string
	repo GithubRepo // this isn't going to be memory efficient
	Meta string // full payload of json object
}

type Treeish struct {
	Label string
	Sha string
	Repo GithubApiRepo
	Html_url string
	User GithubUser
}

type Commit struct {
	Sha string
	Message string
	Author struct {
		Name string
		Email string
	}
}

func (c *Commit) ShortSha() string {
	return c.Sha[0:6]
}

func (c *Commit) ShortCommit() string {
	msg := strings.Split(c.Message, "\n")[0]
	if len(msg) > 80 {
		return msg[0:77] + "..."
	}
	return msg
}

// Nullable String
type NString string

func (n *NString) UnmarshalJSON(b []byte) (err error) {
	if string(b) == "null" {
		return nil
	}
	return json.Unmarshal(b, (*string)(n))
}

// get user's followed users
// get user's followed repos
// for each ^, pull out activity (goroutine)
//   return rendered template bits
// join template bits

// Does not take over ownership of db.
func get_users_repos(db *sql.DB, user_id int) ([]GithubRepo, error) {
	var repo_count int

	// TODO(justinabrahms): Should really alter the schema such
	// that the join from user <-> repo doesn't go through
	// userprofiles.
	row := db.QueryRow("SELECT count(*) FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)

	err := row.Scan(&repo_count)
	if (err != nil) {
		return nil, err
	}
	var repos = make([]GithubRepo, repo_count)

	rows, err := db.Query("SELECT r.id, username, project_name FROM streamer_repo r" +
	" JOIN streamer_userprofile_repos upr ON upr.repo_id = r.id " +
	" JOIN streamer_userprofile up ON up.id = upr.userprofile_id" +
	" WHERE up.user_id = ?;", user_id)
	// what's a good way to handle this not working?
	if (err != nil) {
		return nil, err
	}
	defer rows.Close()

	var username, project_name string
	var pk int
	for rows.Next() {
		err = rows.Scan(&pk, &username, &project_name)
		if (err != nil) {
			fmt.Println("Error!: ", err)
		}
		var repo = GithubRepo{pk, username, project_name}
		repos = append(repos, repo)
	}

	return repos, err
}

func get_repo_activity(db *sql.DB, repo *GithubRepo) (activity_list []Activity, err error){
	rows, err := db.Query(
		"SELECT a.id, a.event_id, a.type, a.created_at, ghu.name, r.username, r.project_name, meta" +
		" FROM streamer_activity a" +
		" JOIN streamer_repo r on r.id=a.repo_id" +
		" JOIN streamer_githubuser ghu on ghu.id=a.user_id" +
		" JOIN streamer_userprofile_repos upr on r.id=upr.repo_id" +
		" WHERE r.id = ?" +
		" AND (upr.last_sent is null" + // hasn't been sent at all
		"   OR a.created_at > upr.last_sent)",  // or hasn't been sent since we've gotten new stuff
		repo.Id)
	if (err != nil) {
		return nil, err
	}
	defer rows.Close()

	activity_list = make([]Activity, 0)

	var (
		pk, github_id, repo_id int
		activity_type, username, repo_project, repo_user, meta, created_str string
		created_at time.Time
	)

	for rows.Next() {
		err = rows.Scan(&pk, &github_id, &activity_type, &created_str, &username, &repo_user, &repo_project, &meta)
		if (err != nil) {
			fmt.Println("ERR: ", err)
		}

		created_at, err = time.Parse("2006-01-02 15:04:05", created_str)
		if (err != nil) {
			fmt.Println("Can't parse the format of ", created_str)
			return nil, err
		}

		activity_list = append(activity_list,
			Activity{pk, github_id, activity_type, created_at, username,
			GithubRepo{repo_id, repo_user, repo_project}, meta})
	}

	return
}

func commit_comment_render(activities []Activity, long_template bool) string { return "" }
func pull_request_comment_render(activities []Activity, long_template bool) string { return "" }
func member_render(activities []Activity, long_template bool) string { return "" }
func download_render(activities []Activity, long_template bool) string { return "" }
func fork_apply_render(activities []Activity, long_template bool) string { return "" }
func team_add_render(activities []Activity, long_template bool) string { return "" }
func gist_render(activities []Activity, long_template bool) string { return "" }
func follow_render(activities []Activity, long_template bool) string { return "" }

func repo_to_template(repo GithubRepo, activities []Activity, render_map map[string]func([]Activity, bool)string) string {
	if len(activities) == 0 {
		return ""
	}
	var activity_map = make(map[string][]Activity)
	for _, activity := range activities {
		// This seems like a lot of juggling. Is there a better way?
		arr := activity_map[activity.activity_type]
		if (arr == nil) {
			arr = make([]Activity, 0)
			activity_map[activity.activity_type] = arr
		}
		arr = append(arr, activity)
		activity_map[activity.activity_type] = arr
	}

	// activity_map: activity_type => []activity
	var response = fmt.Sprintf("%s:\n", repo.FullName())
	for activity_type, activities := range activity_map {
		fn, ok := render_map[activity_type]
		if ok {
			response += fn(activities, true)
		} else {
			log.Print("Not sure how to render activites of type ", activity_type)
		}
	}
	return response
}

func repo_to_string(db *sql.DB, repo GithubRepo, response chan string) {
	activities, err := get_repo_activity(db, &repo)
	if (err != nil) {
		fmt.Println("ERR: ", err)
		os.Exit(1)
	}

	activity_type_to_renderer := map[string]func([]Activity, bool)string {
		"P": push_render,
		"PR": pull_request_render,
		"D": delete_render,
		"C": create_render,
		"W": watch_render,
		"F": fork_render,
		"IC": issue_comment_render,
		"Gl": wiki_render, // Gl is for Gollum, Github's wiki thing.
		"I": issue_render,
		"Pb": public_render,
		// TODO: TA (team add), FA (fork apply),
		// TODO: DO (Download), RC (Pull Request Review Comment),
		// TODO: Fl (Follow), G (Gist)
	}

	response <- repo_to_template(repo, activities, activity_type_to_renderer)
}

func mark_user_repo_sent(db *sql.DB, user User, repos []GithubRepo) (err error) {
	// should find the streamer_userprofile_repo row for the repo
	// / user combo, mark its last_sent as now
	ids := make([]string, 0)
	for _, repo := range repos {
		// why are they 0?
		if repo.Id != 0 {
			ids = append(ids, strconv.FormatInt(int64(repo.Id), 10))
		}
	}

	// There is likely a better way to get parameterization, but
	// it wasn't working for me with ?'s.
	str := fmt.Sprintf(
		"UPDATE streamer_userprofile_repos " +
		"SET last_sent=NOW() " +
		"WHERE repo_id IN (%s)", strings.Join(ids, ","))
	_, err = db.Exec(str)
	return
}

var user_id = flag.Int("user_id", 1, "ID of user to output.")

// TODO: Github Users
// TODO: Email people
// TODO: Limit information by time (past day or w/e)

func main() {
	flag.Parse()

	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/%s?charset=utf8", os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_DB")))
	if (err != nil) {
		log.Fatalf("Unable to connect to mysql. ", err)
	}
	defer db.Close()

	repos, err := get_users_repos(db, *user_id)
	if err != nil {
		log.Fatalf("Error fetching user's repos. %s", err)
	}

	fmt.Println("Got ", len(repos), " repos.")

	response_chan := make(chan string, len(repos))
	for _, repo := range repos {
		repo_to_string(db, repo, response_chan)
	}

	var response string
	for num_responses := 0; num_responses < len(repos); {
		select {
		case r := <-response_chan:
			num_responses++
			response += r
		}
	}

	user := User{*user_id, ""}
	err = mark_user_repo_sent(db, user, repos)
	if err != nil {
		log.Fatalf("Error updating repositories as sent.")
	}

	fmt.Println(response)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"

	arg "github.com/alexflint/go-arg"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/common/model"
	"github.com/tucnak/telebot"
)

const (
	commandStart = "/start"
	commandStop  = "/stop"
	commandHelp  = "/help"

	responseStart = "Hey, %s! I will now keep you up to date!\n" + commandHelp
	responseStop  = "Alright, %s! I won't talk to you again.\n" + commandHelp
	responseHelp  = `
I'm a drone.io bot. I can notify you about your builds.

Available commands:
` + commandStart + ` - Start listening for drone.io builds
` + commandStop + `- Stop listening for drone.io builds
`
)

var users map[int]telebot.User

type Config struct {
	TelegramToken string `arg:"env:TELEGRAM_TOKEN"`
	TelegramAdmin int    `arg:"env:TELEGRAM_ADMIN"`
}

func main() {
	log.Println("starting...")

	var c Config
	arg.MustParse(&c)

	bot, err := telebot.NewBot(c.TelegramToken)
	if err != nil {
		log.Fatalln(err)
	}

	users = make(map[int]telebot.User)
	messages := make(chan telebot.Message, 100)
	bot.Listen(messages, 1*time.Second)

	go HTTPListenAndServe(bot)

	for message := range messages {
		if message.Sender.ID != c.TelegramAdmin {
			log.Printf("dropped message from unallowed sender: %s(%d)", message.Sender.Username, message.Sender.ID)
			continue
		}

		switch message.Text {
		case commandStart:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStart, message.Sender.FirstName), nil)
			users[message.Sender.ID] = message.Sender
			log.Printf("User %s(%d) subscribed", message.Sender.Username, message.Sender.ID)
		case commandStop:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStop, message.Sender.FirstName), nil)
			delete(users, message.Sender.ID)
			log.Printf("User %s(%d) unsubscribed", message.Sender.Username, message.Sender.ID)
		case commandHelp:
			bot.SendMessage(message.Chat, responseHelp, nil)
		}
	}
}

func HTTPListenAndServe(bot *telebot.Bot) {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var webhook notify.WebhookMessage

		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&webhook)
		if err != nil {
			log.Printf("failed to decode webhook message: %v\n", err)
		}
		defer r.Body.Close()

		jsonWebhook, err := json.Marshal(webhook)
		if err != nil {
			log.Printf("failed to encode webhook for logging: %v", err)
		}
		log.Println(string(jsonWebhook))

		for _, alert := range webhook.Alerts {
			status := alert.Status
			switch status {
			case string(model.AlertFiring):
				status = "🔥 *" + strings.ToUpper(status) + "* 🔥"
			case string(model.AlertResolved):
				status = "✅ *" + strings.ToUpper(status) + "* ✅"
			}

			message := fmt.Sprintf(
				"%s\n*%s* (%s)\n%s",
				status,
				alert.Labels["alertname"],
				alert.Annotations["summary"],
				alert.Annotations["description"],
			)

			for _, user := range users {
				bot.SendMessage(user, message, &telebot.SendOptions{ParseMode: telebot.ModeMarkdown})
			}

		}

		w.WriteHeader(http.StatusOK)
	})

	log.Fatalln(http.ListenAndServe(":8080", nil))
}
<commit_msg>Log raw request.Body to stdout<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"

	arg "github.com/alexflint/go-arg"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/common/model"
	"github.com/tucnak/telebot"
)

const (
	commandStart = "/start"
	commandStop  = "/stop"
	commandHelp  = "/help"

	responseStart = "Hey, %s! I will now keep you up to date!\n" + commandHelp
	responseStop  = "Alright, %s! I won't talk to you again.\n" + commandHelp
	responseHelp  = `
I'm a drone.io bot. I can notify you about your builds.

Available commands:
` + commandStart + ` - Start listening for drone.io builds
` + commandStop + `- Stop listening for drone.io builds
`
)

var users map[int]telebot.User

type Config struct {
	TelegramToken string `arg:"env:TELEGRAM_TOKEN"`
	TelegramAdmin int    `arg:"env:TELEGRAM_ADMIN"`
}

func main() {
	log.Println("starting...")

	var c Config
	arg.MustParse(&c)

	bot, err := telebot.NewBot(c.TelegramToken)
	if err != nil {
		log.Fatalln(err)
	}

	users = make(map[int]telebot.User)
	messages := make(chan telebot.Message, 100)
	bot.Listen(messages, 1*time.Second)

	go HTTPListenAndServe(bot)

	for message := range messages {
		if message.Sender.ID != c.TelegramAdmin {
			log.Printf("dropped message from unallowed sender: %s(%d)", message.Sender.Username, message.Sender.ID)
			continue
		}

		switch message.Text {
		case commandStart:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStart, message.Sender.FirstName), nil)
			users[message.Sender.ID] = message.Sender
			log.Printf("User %s(%d) subscribed", message.Sender.Username, message.Sender.ID)
		case commandStop:
			bot.SendMessage(message.Chat, fmt.Sprintf(responseStop, message.Sender.FirstName), nil)
			delete(users, message.Sender.ID)
			log.Printf("User %s(%d) unsubscribed", message.Sender.Username, message.Sender.ID)
		case commandHelp:
			bot.SendMessage(message.Chat, responseHelp, nil)
		}
	}
}

func HTTPListenAndServe(bot *telebot.Bot) {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var webhook notify.WebhookMessage

		var buf bytes.Buffer
		tee := io.TeeReader(r.Body, &buf)
		defer r.Body.Close()

		decoder := json.NewDecoder(tee)
		if err := decoder.Decode(&webhook); err != nil {
			log.Printf("failed to decode webhook message: %v\n", err)
		}

		body, err := ioutil.ReadAll(&buf)
		if err != nil {
			log.Printf("failed to read from request.Body for logging: %v", err)
		}
		log.Println(string(body))

		for _, alert := range webhook.Alerts {
			status := alert.Status
			switch status {
			case string(model.AlertFiring):
				status = "🔥 *" + strings.ToUpper(status) + "* 🔥"
			case string(model.AlertResolved):
				status = "✅ *" + strings.ToUpper(status) + "* ✅"
			}

			message := fmt.Sprintf(
				"%s\n*%s* (%s)\n%s",
				status,
				alert.Labels["alertname"],
				alert.Annotations["summary"],
				alert.Annotations["description"],
			)

			for _, user := range users {
				bot.SendMessage(user, message, &telebot.SendOptions{ParseMode: telebot.ModeMarkdown})
			}

		}

		w.WriteHeader(http.StatusOK)
	})

	log.Fatalln(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>//     Copyright (C) 2014  George Shammas
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/go.crypto/ssh/terminal"
	"flag"
	"github.com/golang/glog"
	"io/ioutil"
	//"reflect"
	"runtime"
	"strconv"
	"time"
)

var rsaPrivKeyFile = flag.String("rsa_key", "id_rsa", "The private rsa key for the ssh-server")
var dsaPrivKeyFile = flag.String("dsa_key", "id_dsa", "The private dsa key for the ssh-server")
var ecdsaPrivKeyFile = flag.String("ecdsa_key", "id_ecdsa", "The private ecdsa key for the ssh-server")
var bumpEvery = flag.Int("bump_every", 3600, "How offten to print general process stats, in seconds")
var port = flag.Int("port", 2022, "The port to listen on")

func init() {
}

func Bumper() {
	ticker := time.NewTicker(time.Duration(*bumpEvery) * time.Second)
	var mstats runtime.MemStats
	for {
		runtime.ReadMemStats(&mstats)
		off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		glog.Infof("GoRoutines: %v Alloc: %vBytes LastGCPause: %v AvgGCPause: %v",
			runtime.NumGoroutine(), mstats.Alloc, time.Duration(mstats.PauseNs[off]),
			time.Duration(mstats.PauseTotalNs/uint64(mstats.NumGC)))

		<-ticker.C

	}
}

func ServeSSHConnection(sConn *ssh.ServerConn) {
	defer sConn.Close()
	defer glog.Infof("Broke Down connection with %v", sConn.RemoteAddr())

	sCloseChan := make(chan struct{})
	defer close(sCloseChan)

	for {
		// Accept reads from the connection, demultiplexes packets
		// to their corresponding channels and returns when a new
		// channel request is seen. Some goroutine must always be
		// calling Accept; otherwise no messages will be forwarded
		// to the channels.
		channel, err := sConn.Accept()
		if err != nil {
			glog.Errorf("error from Accept from %v with error %v: ", sConn.RemoteAddr(), err)
			break
		}

		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if channelType := channel.ChannelType(); channelType != "session" {
			glog.Warningf("Rejected Channel from %v because of unknown channel type %q", sConn.RemoteAddr(), channelType)
			channel.Reject(ssh.UnknownChannelType, "unknown channel type "+channelType)
			continue
		}
		go func() {
			channel.Accept()
			defer channel.Close()
			defer glog.Infof("Broke Down Channel from %v", sConn.RemoteAddr())

			cCloseChan := make(chan struct{})
			defer close(cCloseChan)

			glog.Warningf("Creating Channel from %v with Payload: %q", sConn.RemoteAddr(), channel.ExtraData())

			term := terminal.NewTerminal(channel, "> ")
			serverTerm := &ssh.ServerTerminal{
				Term:    term,
				Channel: channel,
			}
			/*
				go func() {
					for {
						var data []byte
						length, err := channel.Read(data)
						if errT:= reflect.TypeOf(err); errT.String() == "ssh.ChannelRequest" {
							request := err.(ssh.ChannelRequest)
							glog.Warningf("Got a channel request from %v of %v: ", sConn.RemoteAddr(), request)
						} else if err != nil {
							glog.Errorf("Got a werid channel read from %v with error: %v", sConn.RemoteAddr(), err)
							break
						} else {
							glog.Warningf("Got a channel read from %v with data %q: ", sConn.RemoteAddr(), data[:length])
						}
					}
				}()
			*/
			for {
				line, err := serverTerm.ReadLine()
				if err != nil {
					return
				}
				glog.Info(line)
				if line == "quit" {
					return
				}
			}

		}()
	}
}

func main() {
	flag.Parse()

	go Bumper()

	// An SSH server is represented by a ServerConfig, which holds
	// certificate details and handles authentication of ServerConns.
	config := &ssh.ServerConfig{
		NoClientAuth: false,
		PasswordCallback: func(conn *ssh.ServerConn, user, pass string) bool {
			glog.Warningf("Password auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		PublicKeyCallback: func(conn *ssh.ServerConn, user, algo string, pubkey []byte) bool {
			glog.Warningf("Pubkey auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		KeyboardInteractiveCallback: func(conn *ssh.ServerConn, user string, client ssh.ClientKeyboardInteractive) bool {
			glog.Warningf("Interactive auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
	}

	var keyFiles []*string
	keyFiles = append(keyFiles, rsaPrivKeyFile, dsaPrivKeyFile, ecdsaPrivKeyFile)
	for _, keyFile := range keyFiles {
		pemBytes, err := ioutil.ReadFile(*keyFile)
		if err != nil {
			glog.Warning("Failed to load private key: ", err)
			continue
		}
		if signer, err := ssh.ParsePrivateKey(pemBytes); err != nil {
			glog.Fatal("Failed to parse private key: ", err)
		} else {
			glog.Info("Added private key ", keyFile)
			config.AddHostKey(signer)
		}
	}

	// Once a ServerConfig has been configured, connections can be
	// accepted.
	listener, err := ssh.Listen("tcp", "0.0.0.0:"+strconv.Itoa(*port), config)
	if err != nil {
		glog.Fatalf("failed to listen for connection: %v", err)
	}

	for {
		sConn, err := listener.Accept()
		if err != nil {
			glog.Errorf("failed to accept incoming connection from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}
		if err := sConn.Handshake(); err != nil {
			glog.Errorf("failed to handshake from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}

		// A ServerConn multiplexes several channels, which must
		// themselves be Accepted.
		go ServeSSHConnection(sConn)
	}

	glog.Fatal("How did I get here?")
}
<commit_msg>Make the adding of keys a little prettier<commit_after>//   Copyright (C) 2014  George Shammas
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/go.crypto/ssh/terminal"
	"flag"
	"github.com/golang/glog"
	"io/ioutil"
	//"reflect"
	"runtime"
	"strconv"
	"time"
)

var rsaPrivKeyFile = flag.String("rsa_key", "id_rsa", "The private rsa key for the ssh-server")
var dsaPrivKeyFile = flag.String("dsa_key", "id_dsa", "The private dsa key for the ssh-server")
var ecdsaPrivKeyFile = flag.String("ecdsa_key", "id_ecdsa", "The private ecdsa key for the ssh-server")
var bumpEvery = flag.Int("bump_every", 3600, "How offten to print general process stats, in seconds")
var port = flag.Int("port", 2022, "The port to listen on")

func init() {
}

func Bumper() {
	ticker := time.NewTicker(time.Duration(*bumpEvery) * time.Second)
	var mstats runtime.MemStats
	for {
		runtime.ReadMemStats(&mstats)
		off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		glog.Infof("GoRoutines: %v Alloc: %vBytes LastGCPause: %v AvgGCPause: %v",
			runtime.NumGoroutine(), mstats.Alloc, time.Duration(mstats.PauseNs[off]),
			time.Duration(mstats.PauseTotalNs/uint64(mstats.NumGC)))

		<-ticker.C

	}
}

func ServeSSHConnection(sConn *ssh.ServerConn) {
	defer sConn.Close()
	defer glog.Infof("Broke Down connection with %v", sConn.RemoteAddr())

	sCloseChan := make(chan struct{})
	defer close(sCloseChan)

	for {
		// Accept reads from the connection, demultiplexes packets
		// to their corresponding channels and returns when a new
		// channel request is seen. Some goroutine must always be
		// calling Accept; otherwise no messages will be forwarded
		// to the channels.
		channel, err := sConn.Accept()
		if err != nil {
			glog.Errorf("error from Accept from %v with error %v: ", sConn.RemoteAddr(), err)
			break
		}

		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if channelType := channel.ChannelType(); channelType != "session" {
			glog.Warningf("Rejected Channel from %v because of unknown channel type %q", sConn.RemoteAddr(), channelType)
			channel.Reject(ssh.UnknownChannelType, "unknown channel type "+channelType)
			continue
		}
		go func() {
			channel.Accept()
			defer channel.Close()
			defer glog.Infof("Broke Down Channel from %v", sConn.RemoteAddr())

			cCloseChan := make(chan struct{})
			defer close(cCloseChan)

			glog.Warningf("Creating Channel from %v with Payload: %q", sConn.RemoteAddr(), channel.ExtraData())

			term := terminal.NewTerminal(channel, "> ")
			serverTerm := &ssh.ServerTerminal{
				Term:    term,
				Channel: channel,
			}
			/*
				go func() {
					for {
						var data []byte
						length, err := channel.Read(data)
						if errT:= reflect.TypeOf(err); errT.String() == "ssh.ChannelRequest" {
							request := err.(ssh.ChannelRequest)
							glog.Warningf("Got a channel request from %v of %v: ", sConn.RemoteAddr(), request)
						} else if err != nil {
							glog.Errorf("Got a werid channel read from %v with error: %v", sConn.RemoteAddr(), err)
							break
						} else {
							glog.Warningf("Got a channel read from %v with data %q: ", sConn.RemoteAddr(), data[:length])
						}
					}
				}()
			*/
			for {
				line, err := serverTerm.ReadLine()
				if err != nil {
					return
				}
				glog.Info(line)
				if line == "quit" {
					return
				}
			}

		}()
	}
}

func main() {
	flag.Parse()

	go Bumper()

	// An SSH server is represented by a ServerConfig, which holds
	// certificate details and handles authentication of ServerConns.
	config := &ssh.ServerConfig{
		NoClientAuth: false,
		PasswordCallback: func(conn *ssh.ServerConn, user, pass string) bool {
			glog.Warningf("Password auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		PublicKeyCallback: func(conn *ssh.ServerConn, user, algo string, pubkey []byte) bool {
			glog.Warningf("Pubkey auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		KeyboardInteractiveCallback: func(conn *ssh.ServerConn, user string, client ssh.ClientKeyboardInteractive) bool {
			glog.Warningf("Interactive auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
	}

	// Read all the key files, and add them to the ssh config if they check out
	for _, keyFile := range [3]*string{rsaPrivKeyFile, dsaPrivKeyFile, ecdsaPrivKeyFile} {
		pemBytes, err := ioutil.ReadFile(*keyFile)
		if err != nil {
			glog.Warning("Failed to load private key: ", err)
			continue
		}
		if signer, err := ssh.ParsePrivateKey(pemBytes); err != nil {
			glog.Fatal("Failed to parse private key: ", err)
		} else {
			glog.Info("Added private ", signer.PublicKey().PrivateKeyAlgo(), " key.")
			config.AddHostKey(signer)
		}
	}

	// Once a ServerConfig has been configured, connections can be
	// accepted.
	listener, err := ssh.Listen("tcp", "0.0.0.0:"+strconv.Itoa(*port), config)
	if err != nil {
		glog.Fatalf("failed to listen for connection: %v", err)
	}

	for {
		sConn, err := listener.Accept()
		if err != nil {
			glog.Errorf("failed to accept incoming connection from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}
		if err := sConn.Handshake(); err != nil {
			glog.Errorf("failed to handshake from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}

		// A ServerConn multiplexes several channels, which must
		// themselves be Accepted.
		go ServeSSHConnection(sConn)
	}

	glog.Fatal("How did I get here?")
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
)

type gitHandler struct {
	method      string
	regexp      *regexp.Regexp
	handle_func func(string, string, string, http.ResponseWriter, *http.Request)
	rpc         string
}

var http_client = &http.Client{}
var path_traversal = regexp.MustCompile(`/../`)

// Command-line options
var repo_root string
var listen_addr = flag.String("listen_addr", "localhost:8181", "Listen address for HTTP server")
var auth_backend = flag.String("auth_backend", "http://localhost:8080", "Authentication/authorization backend")

var git_handlers = [...]gitHandler{
	gitHandler{"GET", regexp.MustCompile(`\A(/..*)/info/refs\z`), handle_get_info_refs, ""},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-upload-pack\z`), handle_post_rpc, "git-upload-pack"},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-receive-pack\z`), handle_post_rpc, "git-receive-pack"},
}

func main() {
	// Parse the command-line
	flag.Parse()
	repo_root = flag.Arg(0)
	log.Printf("repo_root: %s", repo_root)

	http.HandleFunc("/", git_handler)
	log.Fatal(http.ListenAndServe(*listen_addr, nil))
}

func git_handler(w http.ResponseWriter, r *http.Request) {
	var user string

	log.Print(r.Method, " ", r.URL)

	for _, g := range git_handlers {
		path_match := g.regexp.FindStringSubmatch(r.URL.Path)
		if r.Method == g.method && path_match != nil {
			found_path := path_match[1]
			if !valid_path(found_path) {
				http.Error(w, "Not found", 404)
				return
			}
			auth_response, err := do_auth_request(r)
			if err != nil {
				fail_500(w, err)
				return
			}
			if auth_response.StatusCode != 200 {
				// The Git request is not allowed by the backend. Maybe the client
				// needs to send HTTP Basic credentials.
				// Forward the response from the auth backend to our client
				for k, v := range auth_response.Header {
					w.Header()[k] = v
				}
				w.WriteHeader(auth_response.StatusCode)
				io.Copy(w, auth_response.Body)
				return
			}
			// The auth backend told us who the user is according to them
			if _, err := fmt.Fscan(auth_response.Body, &user); err != nil {
				fail_500(w, err)
				return
			}
			g.handle_func(user, g.rpc, path.Join(repo_root, found_path), w, r)
			return
		}
	}
	http.Error(w, "Not found", 404)
}

func valid_path(p string) bool {
	if path_traversal.MatchString(p) {
		log.Printf("path traversal detected in %s", p)
		return false
	}
	if _, err := os.Stat(path.Join(repo_root, p, "objects")); err != nil {
		log.Print(err)
		return false
	}
	return true
}

func do_auth_request(r *http.Request) (result *http.Response, err error) {
	url := fmt.Sprintf("%s%s", *auth_backend, r.URL.RequestURI())
	auth_req, err := http.NewRequest(r.Method, url, nil)
	if err != nil {
		return nil, err
	}
	for k, v := range r.Header {
		auth_req.Header[k] = v
	}
	result, err = http_client.Do(auth_req)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func handle_get_info_refs(user string, _ string, path string, w http.ResponseWriter, r *http.Request) {
	rpc := r.URL.Query().Get("service")
	switch rpc {
	case "git-upload-pack", "git-receive-pack":
		cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", "--advertise-refs", path)
		set_cmd_env(cmd, user)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fail_500(w, err)
			return
		}
		defer stdout.Close()
		if err := cmd.Start(); err != nil {
			fail_500(w, err)
			return
		}
		w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
		no_cache(w)
		w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic
		if err := pkt_line(w, fmt.Sprintf("# service=%s\n", rpc)); err != nil {
			panic(err)
		}
		if err := pkt_flush(w); err != nil {
			panic(err)
		}
		if _, err := io.Copy(w, stdout); err != nil {
			panic(err) // No point in sending 500 to the client
		}
		if err := cmd.Wait(); err != nil {
			panic(err) // No point in sending 500 to the client
		}
	case "":
		// The 'dumb' Git HTTP protocol is not supported
		http.Error(w, "Not found", 404)
	}
}

func set_cmd_env(cmd *exec.Cmd, user string) {
	cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)}
}

func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter, r *http.Request) {
	var body io.Reader
	var err error

	if r.Header.Get("Content-Encoding") == "gzip" {
		body, err = gzip.NewReader(r.Body)
		if err != nil {
			fail_500(w, err)
			return
		}
	} else {
		body = r.Body
	}
	cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", path)
	set_cmd_env(cmd, user)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	defer stdout.Close()
	stdin, err := cmd.StdinPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	defer stdin.Close()
	if err := cmd.Start(); err != nil {
		fail_500(w, err)
		return
	}
	if _, err := io.Copy(stdin, body); err != nil {
		fail_500(w, err)
		return
	}
	stdin.Close()

	w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", rpc))
	no_cache(w)
	w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic
	if _, err := io.Copy(w, stdout); err != nil {
		panic(err) // No point in sending 500 to the client
	}
	if err := cmd.Wait(); err != nil {
		panic(err) // No point in sending 500 to the client
	}
}

func pkt_line(w io.Writer, s string) error {
	_, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s)
	return err
}

func pkt_flush(w io.Writer) error {
	_, err := fmt.Fprint(w, "0000")
	return err
}

func fail_500(w http.ResponseWriter, err error) {
	http.Error(w, "Internal server error", 500)
	log.Print(err)
}

func no_cache(w http.ResponseWriter) {
	w.Header().Add("Cache-Control", "no-cache")
}
<commit_msg>Rename stuff, comments<commit_after>package main

import (
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
)

type gitService struct {
	method      string
	regexp      *regexp.Regexp
	handle_func func(string, string, string, http.ResponseWriter, *http.Request)
	rpc         string
}

var http_client = &http.Client{}
var path_traversal = regexp.MustCompile(`/../`)

// Command-line options
var repo_root = "."
var listen_addr = flag.String("listen_addr", "localhost:8181", "Listen address for HTTP server")
var auth_backend = flag.String("auth_backend", "http://localhost:8080", "Authentication/authorization backend")

var git_services = [...]gitService{
	gitService{"GET", regexp.MustCompile(`\A(/..*)/info/refs\z`), handle_get_info_refs, ""},
	gitService{"POST", regexp.MustCompile(`\A(/..*)/git-upload-pack\z`), handle_post_rpc, "git-upload-pack"},
	gitService{"POST", regexp.MustCompile(`\A(/..*)/git-receive-pack\z`), handle_post_rpc, "git-receive-pack"},
}

func main() {
	// Parse the command-line
	flag.Parse()
	repo_root = flag.Arg(0)
	log.Printf("repo_root: %s", repo_root)

	http.HandleFunc("/", git_handler)
	log.Fatal(http.ListenAndServe(*listen_addr, nil))
}

func git_handler(w http.ResponseWriter, r *http.Request) {
	var user string

	log.Print(r.Method, " ", r.URL)

	for _, g := range git_services {
		path_match := g.regexp.FindStringSubmatch(r.URL.Path)
		if r.Method == g.method && path_match != nil {
			found_path := path_match[1]
			if !valid_path(found_path) {
				http.Error(w, "Not found", 404)
				return
			}
			auth_response, err := do_auth_request(r)
			if err != nil {
				fail_500(w, err)
				return
			}
			if auth_response.StatusCode != 200 {
				// The Git request is not allowed by the backend. Maybe the
				// client needs to send HTTP Basic credentials.  Forward the
				// response from the auth backend to our client. This includes
				// the 'WWW-Authentication' header that acts as a hint that
				// Basic auth credentials are needed.
				for k, v := range auth_response.Header {
					w.Header()[k] = v
				}
				w.WriteHeader(auth_response.StatusCode)
				io.Copy(w, auth_response.Body)
				return
			}
			// The auth backend told us who the user is according to them
			// (GL_ID). We must extract this information from the auth response
			// body.
			if _, err := fmt.Fscan(auth_response.Body, &user); err != nil {
				fail_500(w, err)
				return
			}
			g.handle_func(user, g.rpc, path.Join(repo_root, found_path), w, r)
			return
		}
	}
	http.Error(w, "Not found", 404)
	return
}

func valid_path(p string) bool {
	if path_traversal.MatchString(p) {
		log.Printf("path traversal detected in %s", p)
		return false
	}
	// If /path/to/foo.git/objects exist then let's assume it is a valid Git
	// repository.
	if _, err := os.Stat(path.Join(repo_root, p, "objects")); err != nil {
		log.Print(err)
		return false
	}
	return true
}

func do_auth_request(r *http.Request) (result *http.Response, err error) {
	url := fmt.Sprintf("%s%s", *auth_backend, r.URL.RequestURI())
	auth_req, err := http.NewRequest(r.Method, url, nil)
	if err != nil {
		return nil, err
	}
	// Forward all headers from our client to the auth backend. This includes
	// HTTP Basic authentication credentials (the 'Authorization' header).
	for k, v := range r.Header {
		auth_req.Header[k] = v
	}
	return http_client.Do(auth_req)
}

func handle_get_info_refs(user string, _ string, path string, w http.ResponseWriter, r *http.Request) {
	rpc := r.URL.Query().Get("service")
	switch rpc {
	case "git-upload-pack", "git-receive-pack":
		cmd := exec.Command("git", sub_command(rpc), "--stateless-rpc", "--advertise-refs", path)
		set_cmd_env(cmd, user)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fail_500(w, err)
			return
		}
		defer stdout.Close()
		if err := cmd.Start(); err != nil {
			fail_500(w, err)
			return
		}
		w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
		header_no_cache(w)
		w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic
		if err := pkt_line(w, fmt.Sprintf("# service=%s\n", rpc)); err != nil {
			panic(err)
		}
		if err := pkt_flush(w); err != nil {
			panic(err)
		}
		if _, err := io.Copy(w, stdout); err != nil {
			panic(err)
		}
		if err := cmd.Wait(); err != nil {
			panic(err)
		}
	case "":
		// The 'dumb' Git HTTP protocol is not supported
		http.Error(w, "Not found", 404)
	}
}

func sub_command(rpc string) string {
	return strings.TrimPrefix(rpc, "git-")
}

func set_cmd_env(cmd *exec.Cmd, user string) {
	cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)}
}

func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter, r *http.Request) {
	var body io.Reader
	var err error

	if r.Header.Get("Content-Encoding") == "gzip" {
		body, err = gzip.NewReader(r.Body)
		if err != nil {
			fail_500(w, err)
			return
		}
	} else {
		body = r.Body
	}

	cmd := exec.Command("git", sub_command(rpc), "--stateless-rpc", path)
	set_cmd_env(cmd, user)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	defer stdout.Close()
	stdin, err := cmd.StdinPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	defer stdin.Close()
	if err := cmd.Start(); err != nil {
		fail_500(w, err)
		return
	}
	if _, err := io.Copy(stdin, body); err != nil {
		fail_500(w, err)
		return
	}
	stdin.Close()

	w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", rpc))
	header_no_cache(w)
	w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic
	if _, err := io.Copy(w, stdout); err != nil {
		panic(err)
	}
	if err := cmd.Wait(); err != nil {
		panic(err)
	}
}

func pkt_line(w io.Writer, s string) error {
	_, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s)
	return err
}

func pkt_flush(w io.Writer) error {
	_, err := fmt.Fprint(w, "0000")
	return err
}

func fail_500(w http.ResponseWriter, err error) {
	http.Error(w, "Internal server error", 500)
	log.Print(err)
}

func header_no_cache(w http.ResponseWriter) {
	w.Header().Add("Cache-Control", "no-cache")
}
<|endoftext|>


<commit_before>// +build linux

package test

import (
	"testing"
)

func TestIntegration__date(t *testing.T) {
	cmd := Command("date", "-u", "--date", "@0").Trim()
	cmd.EqualT(t, "Thu Jan  1 00:00:00 UTC 1970")
}

func TestIntegration__unknown(t *testing.T) {
	cmd := CertManage("other").Trim()
	cmd.FailedT(t)
}

func TestIntegration__list(t *testing.T) {
	cmd := CertManage("list", "-count").Trim()
	cmd.CmpIntF(t, func(i int) bool { return i > 1 })
}

func TestIntegration__backup(t *testing.T) {
	cmd := CertManage("backup").Trim()
	cmd.PendingT(t, "something wonky with 'ca-certificates.backup/.mozilla' dir")
	cmd.EqualT(t, "Backup completed successfully")
}

// TODO(adam): Need to run -whitelist and -restore
<commit_msg>test/linux: drop pending from mozilla dir<commit_after>// +build linux

package test

import (
	"testing"
)

func TestIntegration__date(t *testing.T) {
	cmd := Command("date", "-u", "--date", "@0").Trim()
	cmd.EqualT(t, "Thu Jan  1 00:00:00 UTC 1970")
}

func TestIntegration__unknown(t *testing.T) {
	cmd := CertManage("other").Trim()
	cmd.FailedT(t)
}

func TestIntegration__list(t *testing.T) {
	cmd := CertManage("list", "-count").Trim()
	cmd.CmpIntF(t, func(i int) bool { return i > 1 })
}

func TestIntegration__backup(t *testing.T) {
	cmd := CertManage("backup").Trim()
	cmd.EqualT(t, "Backup completed successfully")
}

// TODO(adam): Need to run -whitelist and -restore
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gorilla/handlers"
)

func main() {
	listen := flag.String("listen", ":8080", "address:port to listen on.")
	bucket := flag.String("bucket", "", "S3 bucket name")
	logRequests := flag.Bool("log-requests", true, "log HTTP requests")
	flag.Parse()

	if *bucket == "" {
		log.Fatalf("bucket name required")
	}

	svc := s3.New(&aws.Config{
		Region: "us-east-1",
	})

	var h http.Handler
	h = &Proxy{
		Bucket: *bucket,
		Svc:    svc,
	}
	if *logRequests {
		h = handlers.CombinedLoggingHandler(os.Stdout, h)
	}

	s := &http.Server{
		Addr:           *listen,
		Handler:        h,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	log.Printf("listening on %s", *listen)
	log.Fatal(s.ListenAndServe())

}
<commit_msg>make region configurable<commit_after>package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gorilla/handlers"
)

func main() {
	listen := flag.String("listen", ":8080", "address:port to listen on.")
	bucket := flag.String("bucket", "", "S3 bucket name")
	logRequests := flag.Bool("log-requests", true, "log HTTP requests")
	region := flag.String("region", "us-east-1", "AWS S3 Region")
	flag.Parse()

	if *bucket == "" {
		log.Fatalf("bucket name required")
	}

	svc := s3.New(&aws.Config{
		Region: *region,
	})

	var h http.Handler
	h = &Proxy{
		Bucket: *bucket,
		Svc:    svc,
	}
	if *logRequests {
		h = handlers.CombinedLoggingHandler(os.Stdout, h)
	}

	s := &http.Server{
		Addr:           *listen,
		Handler:        h,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	log.Printf("listening on %s", *listen)
	log.Fatal(s.ListenAndServe())

}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Apprenda, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/ngpitt/blinkt"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/cache"
)

const (
	added     = iota
	updated   = iota
	deleted   = iota
	unchanged = iota
)

type ColorFunc func(obj interface{}) string

type Controller interface {
	Watch(listWatch *cache.ListWatch, objType runtime.Object, resyncPeriod time.Duration, colorFunc ColorFunc)
	Cleanup()
}

type ControllerObj struct {
	brightness   float64
	resourceList []resource
	resourceLock *sync.Mutex
	blinkt       blinkt.Blinkt
}

type resource struct {
	key   string
	color string
	state int
}

func NewController(brightness float64) Controller {
	return &ControllerObj{
		brightness,
		[]resource{},
		&sync.Mutex{},
		blinkt.NewBlinkt(blinkt.Blue, brightness),
	}
}

func (o *ControllerObj) Watch(listWatch *cache.ListWatch, objType runtime.Object, resyncPeriod time.Duration, colorFunc ColorFunc) {
	_, controller := cache.NewInformer(
		listWatch,
		objType,
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				o.resourceLock.Lock()
				defer o.resourceLock.Unlock()
				key := keyFunc(obj)
				color := colorFunc(obj)
				r := resource{key, color, added}
				log.Print("Adding ", r.key, "...\n")
				o.resourceList = append(o.resourceList, r)
				o.updateBlinkt()
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				o.resourceLock.Lock()
				defer o.resourceLock.Unlock()
				key := keyFunc(newObj)
				color := colorFunc(newObj)
				r := o.getResource(key)
				if color == r.color {
					return
				}
				log.Print("Updating ", r.key, "...\n")
				r.color = color
				r.state = updated
				o.updateBlinkt()
			},
			DeleteFunc: func(obj interface{}) {
				o.resourceLock.Lock()
				defer o.resourceLock.Unlock()
				key := keyFunc(obj)
				r := o.getResource(key)
				log.Println("Deleting ", r.key, "...\n")
				r.state = deleted
				o.updateBlinkt()
			},
		},
	)
	sigs := make(chan os.Signal)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	stopCh := make(chan struct{})
	go func() {
		<-sigs
		log.Println("Stopping the Blinkt controller...")
		close(stopCh)
	}()
	log.Println("Starting the Blinkt controller...")
	controller.Run(stopCh)
}

func (o *ControllerObj) Cleanup() {
	o.blinkt.Cleanup(blinkt.Red, o.brightness)
}

func (o *ControllerObj) getResource(key string) *resource {
	for i, r := range o.resourceList {
		if r.key == key {
			return &o.resourceList[i]
		}
	}
	return nil
}

func (o *ControllerObj) updateBlinkt() {
	i := 0
	for ; i < len(o.resourceList); i++ {
		r := &o.resourceList[i]
		switch r.state {
		case added:
			fallthrough
		case updated:
			if i < 8 {
				o.blinkt.Flash(i, r.color, o.brightness, 2, 50*time.Millisecond)
				o.blinkt.Set(i, r.color, o.brightness)
			}
			r.state = unchanged
		case deleted:
			if i < 8 {
				o.blinkt.Flash(i, r.color, o.brightness, 2, 50*time.Millisecond)
			}
			o.resourceList = append(o.resourceList[:i], o.resourceList[i+1:]...)
			i--
		case unchanged:
			if i < 8 {
				o.blinkt.Set(i, r.color, o.brightness)
			}
		}
	}
	for ; i < 8; i++ {
		o.blinkt.Set(i, blinkt.Off, 0)
	}
	o.blinkt.Show()
}

func keyFunc(obj interface{}) string {
	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
	if err != nil {
		log.Panicln(err.Error())
	}
	return key
}
<commit_msg>Fixed logging<commit_after>// Copyright (c) 2017 Apprenda, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/ngpitt/blinkt"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/cache"
)

const (
	added     = iota
	updated   = iota
	deleted   = iota
	unchanged = iota
)

type ColorFunc func(obj interface{}) string

type Controller interface {
	Watch(listWatch *cache.ListWatch, objType runtime.Object, resyncPeriod time.Duration, colorFunc ColorFunc)
	Cleanup()
}

type ControllerObj struct {
	brightness   float64
	resourceList []resource
	resourceLock *sync.Mutex
	blinkt       blinkt.Blinkt
}

type resource struct {
	key   string
	color string
	state int
}

func NewController(brightness float64) Controller {
	return &ControllerObj{
		brightness,
		[]resource{},
		&sync.Mutex{},
		blinkt.NewBlinkt(blinkt.Blue, brightness),
	}
}

func (o *ControllerObj) Watch(listWatch *cache.ListWatch, objType runtime.Object, resyncPeriod time.Duration, colorFunc ColorFunc) {
	_, controller := cache.NewInformer(
		listWatch,
		objType,
		resyncPeriod,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				o.resourceLock.Lock()
				defer o.resourceLock.Unlock()
				key := keyFunc(obj)
				color := colorFunc(obj)
				r := resource{key, color, added}
				log.Print("Adding ", r.key, "...\n")
				o.resourceList = append(o.resourceList, r)
				o.updateBlinkt()
			},
			UpdateFunc: func(oldObj, newObj interface{}) {
				o.resourceLock.Lock()
				defer o.resourceLock.Unlock()
				key := keyFunc(newObj)
				color := colorFunc(newObj)
				r := o.getResource(key)
				if color == r.color {
					return
				}
				log.Print("Updating ", r.key, "...\n")
				r.color = color
				r.state = updated
				o.updateBlinkt()
			},
			DeleteFunc: func(obj interface{}) {
				o.resourceLock.Lock()
				defer o.resourceLock.Unlock()
				key := keyFunc(obj)
				r := o.getResource(key)
				log.Print("Deleting ", r.key, "...\n")
				r.state = deleted
				o.updateBlinkt()
			},
		},
	)
	sigs := make(chan os.Signal)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	stopCh := make(chan struct{})
	go func() {
		<-sigs
		log.Println("Stopping the Blinkt controller...")
		close(stopCh)
	}()
	log.Println("Starting the Blinkt controller...")
	controller.Run(stopCh)
}

func (o *ControllerObj) Cleanup() {
	o.blinkt.Cleanup(blinkt.Red, o.brightness)
}

func (o *ControllerObj) getResource(key string) *resource {
	for i, r := range o.resourceList {
		if r.key == key {
			return &o.resourceList[i]
		}
	}
	return nil
}

func (o *ControllerObj) updateBlinkt() {
	i := 0
	for ; i < len(o.resourceList); i++ {
		r := &o.resourceList[i]
		switch r.state {
		case added:
			fallthrough
		case updated:
			if i < 8 {
				o.blinkt.Flash(i, r.color, o.brightness, 2, 50*time.Millisecond)
				o.blinkt.Set(i, r.color, o.brightness)
			}
			r.state = unchanged
		case deleted:
			if i < 8 {
				o.blinkt.Flash(i, r.color, o.brightness, 2, 50*time.Millisecond)
			}
			o.resourceList = append(o.resourceList[:i], o.resourceList[i+1:]...)
			i--
		case unchanged:
			if i < 8 {
				o.blinkt.Set(i, r.color, o.brightness)
			}
		}
	}
	for ; i < 8; i++ {
		o.blinkt.Set(i, blinkt.Off, 0)
	}
	o.blinkt.Show()
}

func keyFunc(obj interface{}) string {
	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
	if err != nil {
		log.Panicln(err.Error())
	}
	return key
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"

	cmds "github.com/jbenet/go-ipfs/commands"
	"github.com/jbenet/go-ipfs/core"
	dag "github.com/jbenet/go-ipfs/merkledag"
)

// ErrObjectTooLarge is returned when too much data was read from stdin. current limit 512k
var ErrObjectTooLarge = errors.New("input object was too large. limit is 512kbytes")

const inputLimit = 512 * 1024

var objectCmd = &cmds.Command{
	Description: "Interact with ipfs objects",
	Help:        `'ipfs object' is a plumbing command used to manipulate DAG objects directly.`,

	Subcommands: map[string]*cmds.Command{
		"data":  objectDataCmd,
		"links": objectLinksCmd,
		"get":   objectGetCmd,
		"put":   objectPutCmd,
	},
}

var objectDataCmd = &cmds.Command{
	Description: "Outputs the raw bytes in an IPFS object",
	Help: `ipfs data is a plumbing command for retreiving the raw bytes stored in a DAG node.
It outputs to stdout, and <key> is a base58 encoded multihash.

Note that the "--encoding" option does not affect the output, since the
output is the raw data of the object.
`,

	Arguments: []cmds.Argument{
		cmds.Argument{"key", cmds.ArgString, true, false,
			"Key of the object to retrieve, in base58-encoded multihash format"},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		key, ok := req.Arguments()[0].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		reader, err := objectData(n, key)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(reader)
	},
}

var objectLinksCmd = &cmds.Command{
	Description: "Outputs the links pointed to by the specified object",
	Help: `'ipfs block get' is a plumbing command for retreiving raw IPFS blocks.
It outputs to stdout, and <key> is a base58 encoded multihash.`,

	Arguments: []cmds.Argument{
		cmds.Argument{"key", cmds.ArgString, true, false,
			"Key of the object to retrieve, in base58-encoded multihash format"},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		key, ok := req.Arguments()[0].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		output, err := objectLinks(n, key)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(output)
	},
	Type: &Object{},
}

var objectGetCmd = &cmds.Command{
	Description: "Get and serialize the DAG node named by <key>",
	Help: `'ipfs object get' is a plumbing command for retreiving DAG nodes.
It serializes the DAG node to the format specified by the "--encoding" flag.
It outputs to stdout, and <key> is a base58 encoded multihash.

This command outputs data in the following encodings:
  * "protobuf"
  * "json"
  * "xml"
(Specified by the "--encoding" or "-enc" flags)`,

	Arguments: []cmds.Argument{
		cmds.Argument{"key", cmds.ArgString, true, false,
			"Key of the object to retrieve\n(in base58-encoded multihash format)"},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		key, ok := req.Arguments()[0].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		object, err := objectGet(n, key)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(object)
	},
	Type: &dag.Node{},
	Marshallers: map[cmds.EncodingType]cmds.Marshaller{
		cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) {
			object := res.Output().(*dag.Node)
			return object.Marshal()
		},
	},
}

var objectPutCmd = &cmds.Command{
	Description: "Stores input as a DAG object, outputs its key",
	Help: `'ipfs object put' is a plumbing command for storing DAG nodes.
It reads from stdin, and the output is a base58 encoded multihash.

Data should be in the format specified by <encoding>.
<encoding> may be one of the following:
  * "protobuf"
  * "json"
`,

	Arguments: []cmds.Argument{
		cmds.Argument{"data", cmds.ArgFile, true, false,
			"Data to be stored as a DAG object\nMust be encoded as specified in <encoding>"},
		cmds.Argument{"encoding", cmds.ArgString, true, false,
			"Encoding type of <data>, either \"protobuf\" or \"json\""},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		input, ok := req.Arguments()[0].(io.Reader)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		encoding, ok := req.Arguments()[1].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		output, err := objectPut(n, input, encoding)
		if err != nil {
			errType := cmds.ErrNormal
			if err == ErrUnknownObjectEnc {
				errType = cmds.ErrClient
			}
			res.SetError(err, errType)
			return
		}

		res.SetOutput(output)
	},
	Type: &Object{},
}

// objectData takes a key string and writes out the raw bytes of that node (if there is one)
func objectData(n *core.IpfsNode, key string) (io.Reader, error) {
	dagnode, err := n.Resolver.ResolvePath(key)
	if err != nil {
		return nil, err
	}

	log.Debugf("objectData: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links))

	return bytes.NewReader(dagnode.Data), nil
}

// objectLinks takes a key string and lists the links it points to
func objectLinks(n *core.IpfsNode, key string) (*Object, error) {
	dagnode, err := n.Resolver.ResolvePath(key)
	if err != nil {
		return nil, err
	}

	log.Debugf("objectLinks: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links))

	return getOutput(dagnode)
}

// objectGet takes a key string from args and a format option and serializes the dagnode to that format
func objectGet(n *core.IpfsNode, key string) (*dag.Node, error) {
	dagnode, err := n.Resolver.ResolvePath(key)
	if err != nil {
		return nil, err
	}

	log.Debugf("objectGet: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links))

	return dagnode, nil
}

// objectPut takes a format option, serializes bytes from stdin and updates the dag with that data
func objectPut(n *core.IpfsNode, input io.Reader, encoding string) (*Object, error) {
	var (
		dagnode *dag.Node
		data    []byte
		err     error
	)

	data, err = ioutil.ReadAll(io.LimitReader(input, inputLimit+10))
	if err != nil {
		return nil, err
	}

	if len(data) >= inputLimit {
		return nil, ErrObjectTooLarge
	}

	switch getObjectEnc(encoding) {
	case objectEncodingJSON:
		dagnode = new(dag.Node)
		err = json.Unmarshal(data, dagnode)

	case objectEncodingProtobuf:
		dagnode, err = dag.Decoded(data)

	default:
		return nil, ErrUnknownObjectEnc
	}

	if err != nil {
		return nil, err
	}

	err = addNode(n, dagnode)
	if err != nil {
		return nil, err
	}

	return getOutput(dagnode)
}

// ErrUnknownObjectEnc is returned if a invalid encoding is supplied
var ErrUnknownObjectEnc = errors.New("unknown object encoding")

type objectEncoding string

const (
	objectEncodingJSON     objectEncoding = "json"
	objectEncodingProtobuf                = "protobuf"
)

func getObjectEnc(o interface{}) objectEncoding {
	v, ok := o.(string)
	if !ok {
		// chosen as default because it's human readable
		log.Warning("option is not a string - falling back to json")
		return objectEncodingJSON
	}

	return objectEncoding(v)
}

func getOutput(dagnode *dag.Node) (*Object, error) {
	key, err := dagnode.Key()
	if err != nil {
		return nil, err
	}

	output := &Object{
		Hash:  key.Pretty(),
		Links: make([]Link, len(dagnode.Links)),
	}

	for i, link := range dagnode.Links {
		output.Links[i] = Link{
			Name: link.Name,
			Hash: link.Hash.B58String(),
			Size: link.Size,
		}
	}

	return output, nil
}
<commit_msg>core/commands2: Fixed 'object get' dagnode output format<commit_after>package commands

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"

	cmds "github.com/jbenet/go-ipfs/commands"
	"github.com/jbenet/go-ipfs/core"
	dag "github.com/jbenet/go-ipfs/merkledag"
)

// ErrObjectTooLarge is returned when too much data was read from stdin. current limit 512k
var ErrObjectTooLarge = errors.New("input object was too large. limit is 512kbytes")

const inputLimit = 512 * 1024

type Node struct {
	Links []Link
	Data  []byte
}

var objectCmd = &cmds.Command{
	Description: "Interact with ipfs objects",
	Help:        `'ipfs object' is a plumbing command used to manipulate DAG objects directly.`,

	Subcommands: map[string]*cmds.Command{
		"data":  objectDataCmd,
		"links": objectLinksCmd,
		"get":   objectGetCmd,
		"put":   objectPutCmd,
	},
}

var objectDataCmd = &cmds.Command{
	Description: "Outputs the raw bytes in an IPFS object",
	Help: `ipfs data is a plumbing command for retreiving the raw bytes stored in a DAG node.
It outputs to stdout, and <key> is a base58 encoded multihash.

Note that the "--encoding" option does not affect the output, since the
output is the raw data of the object.
`,

	Arguments: []cmds.Argument{
		cmds.Argument{"key", cmds.ArgString, true, false,
			"Key of the object to retrieve, in base58-encoded multihash format"},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		key, ok := req.Arguments()[0].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		reader, err := objectData(n, key)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(reader)
	},
}

var objectLinksCmd = &cmds.Command{
	Description: "Outputs the links pointed to by the specified object",
	Help: `'ipfs block get' is a plumbing command for retreiving raw IPFS blocks.
It outputs to stdout, and <key> is a base58 encoded multihash.`,

	Arguments: []cmds.Argument{
		cmds.Argument{"key", cmds.ArgString, true, false,
			"Key of the object to retrieve, in base58-encoded multihash format"},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		key, ok := req.Arguments()[0].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		output, err := objectLinks(n, key)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(output)
	},
	Type: &Object{},
}

var objectGetCmd = &cmds.Command{
	Description: "Get and serialize the DAG node named by <key>",
	Help: `'ipfs object get' is a plumbing command for retreiving DAG nodes.
It serializes the DAG node to the format specified by the "--encoding" flag.
It outputs to stdout, and <key> is a base58 encoded multihash.

This command outputs data in the following encodings:
  * "protobuf"
  * "json"
  * "xml"
(Specified by the "--encoding" or "-enc" flags)`,

	Arguments: []cmds.Argument{
		cmds.Argument{"key", cmds.ArgString, true, false,
			"Key of the object to retrieve\n(in base58-encoded multihash format)"},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		key, ok := req.Arguments()[0].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		object, err := objectGet(n, key)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		node := &Node{
			Links: make([]Link, len(object.Links)),
			Data:  object.Data,
		}

		for i, link := range object.Links {
			node.Links[i] = Link{
				Hash: link.Hash.B58String(),
				Name: link.Name,
				Size: link.Size,
			}
		}

		res.SetOutput(node)
	},
	Type: &Node{},
	Marshallers: map[cmds.EncodingType]cmds.Marshaller{
		cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) {
			object := res.Output().(*dag.Node)
			return object.Marshal()
		},
	},
}

var objectPutCmd = &cmds.Command{
	Description: "Stores input as a DAG object, outputs its key",
	Help: `'ipfs object put' is a plumbing command for storing DAG nodes.
It reads from stdin, and the output is a base58 encoded multihash.

Data should be in the format specified by <encoding>.
<encoding> may be one of the following:
  * "protobuf"
  * "json"
`,

	Arguments: []cmds.Argument{
		cmds.Argument{"data", cmds.ArgFile, true, false,
			"Data to be stored as a DAG object\nMust be encoded as specified in <encoding>"},
		cmds.Argument{"encoding", cmds.ArgString, true, false,
			"Encoding type of <data>, either \"protobuf\" or \"json\""},
	},
	Run: func(res cmds.Response, req cmds.Request) {
		n := req.Context().Node

		input, ok := req.Arguments()[0].(io.Reader)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		encoding, ok := req.Arguments()[1].(string)
		if !ok {
			res.SetError(errors.New("cast error"), cmds.ErrNormal)
			return
		}

		output, err := objectPut(n, input, encoding)
		if err != nil {
			errType := cmds.ErrNormal
			if err == ErrUnknownObjectEnc {
				errType = cmds.ErrClient
			}
			res.SetError(err, errType)
			return
		}

		res.SetOutput(output)
	},
	Type: &Object{},
}

// objectData takes a key string and writes out the raw bytes of that node (if there is one)
func objectData(n *core.IpfsNode, key string) (io.Reader, error) {
	dagnode, err := n.Resolver.ResolvePath(key)
	if err != nil {
		return nil, err
	}

	log.Debugf("objectData: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links))

	return bytes.NewReader(dagnode.Data), nil
}

// objectLinks takes a key string and lists the links it points to
func objectLinks(n *core.IpfsNode, key string) (*Object, error) {
	dagnode, err := n.Resolver.ResolvePath(key)
	if err != nil {
		return nil, err
	}

	log.Debugf("objectLinks: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links))

	return getOutput(dagnode)
}

// objectGet takes a key string from args and a format option and serializes the dagnode to that format
func objectGet(n *core.IpfsNode, key string) (*dag.Node, error) {
	dagnode, err := n.Resolver.ResolvePath(key)
	if err != nil {
		return nil, err
	}

	log.Debugf("objectGet: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links))

	return dagnode, nil
}

// objectPut takes a format option, serializes bytes from stdin and updates the dag with that data
func objectPut(n *core.IpfsNode, input io.Reader, encoding string) (*Object, error) {
	var (
		dagnode *dag.Node
		data    []byte
		err     error
	)

	data, err = ioutil.ReadAll(io.LimitReader(input, inputLimit+10))
	if err != nil {
		return nil, err
	}

	if len(data) >= inputLimit {
		return nil, ErrObjectTooLarge
	}

	switch getObjectEnc(encoding) {
	case objectEncodingJSON:
		dagnode = new(dag.Node)
		err = json.Unmarshal(data, dagnode)

	case objectEncodingProtobuf:
		dagnode, err = dag.Decoded(data)

	default:
		return nil, ErrUnknownObjectEnc
	}

	if err != nil {
		return nil, err
	}

	err = addNode(n, dagnode)
	if err != nil {
		return nil, err
	}

	return getOutput(dagnode)
}

// ErrUnknownObjectEnc is returned if a invalid encoding is supplied
var ErrUnknownObjectEnc = errors.New("unknown object encoding")

type objectEncoding string

const (
	objectEncodingJSON     objectEncoding = "json"
	objectEncodingProtobuf                = "protobuf"
)

func getObjectEnc(o interface{}) objectEncoding {
	v, ok := o.(string)
	if !ok {
		// chosen as default because it's human readable
		log.Warning("option is not a string - falling back to json")
		return objectEncodingJSON
	}

	return objectEncoding(v)
}

func getOutput(dagnode *dag.Node) (*Object, error) {
	key, err := dagnode.Key()
	if err != nil {
		return nil, err
	}

	output := &Object{
		Hash:  key.Pretty(),
		Links: make([]Link, len(dagnode.Links)),
	}

	for i, link := range dagnode.Links {
		output.Links[i] = Link{
			Name: link.Name,
			Hash: link.Hash.B58String(),
			Size: link.Size,
		}
	}

	return output, nil
}
<|endoftext|>


<commit_before>// Package dnsserver implements all the interfaces from Caddy, so that CoreDNS can be a servertype plugin.
package dnsserver

import (
	"fmt"
	"log"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/metrics/vars"
	"github.com/coredns/coredns/plugin/pkg/edns"
	"github.com/coredns/coredns/plugin/pkg/rcode"
	"github.com/coredns/coredns/plugin/pkg/trace"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
	ot "github.com/opentracing/opentracing-go"
	"golang.org/x/net/context"
)

// Server represents an instance of a server, which serves
// DNS requests at a particular address (host and port). A
// server is capable of serving numerous zones on
// the same address and the listener may be stopped for
// graceful termination (POSIX only).
type Server struct {
	Addr string // Address we listen on

	server [2]*dns.Server // 0 is a net.Listener, 1 is a net.PacketConn (a *UDPConn) in our case.
	m      sync.Mutex     // protects the servers

	zones       map[string]*Config // zones keyed by their address
	dnsWg       sync.WaitGroup     // used to wait on outstanding connections
	connTimeout time.Duration      // the maximum duration of a graceful shutdown
	trace       trace.Trace        // the trace plugin for the server
	debug       bool               // disable recover()
	classChaos  bool               // allow non-INET class queries
}

// NewServer returns a new CoreDNS server and compiles all plugins in to it. By default CH class
// queries are blocked unless the chaos or proxy is loaded.
func NewServer(addr string, group []*Config) (*Server, error) {

	s := &Server{
		Addr:        addr,
		zones:       make(map[string]*Config),
		connTimeout: 5 * time.Second, // TODO(miek): was configurable
	}

	// We have to bound our wg with one increment
	// to prevent a "race condition" that is hard-coded
	// into sync.WaitGroup.Wait() - basically, an add
	// with a positive delta must be guaranteed to
	// occur before Wait() is called on the wg.
	// In a way, this kind of acts as a safety barrier.
	s.dnsWg.Add(1)

	for _, site := range group {
		if site.Debug {
			s.debug = true
		}
		// set the config per zone
		s.zones[site.Zone] = site
		// compile custom plugin for everything
		var stack plugin.Handler
		for i := len(site.Plugin) - 1; i >= 0; i-- {
			stack = site.Plugin[i](stack)

			// register the *handler* also
			site.registerHandler(stack)

			if s.trace == nil && stack.Name() == "trace" {
				// we have to stash away the plugin, not the
				// Tracer object, because the Tracer won't be initialized yet
				if t, ok := stack.(trace.Trace); ok {
					s.trace = t
				}
			}
			if stack.Name() == "chaos" || stack.Name() == "proxy" {
				s.classChaos = true
			}
		}
		site.pluginChain = stack
	}

	return s, nil
}

// Serve starts the server with an existing listener. It blocks until the server stops.
// This implements caddy.TCPServer interface.
func (s *Server) Serve(l net.Listener) error {
	s.m.Lock()
	s.server[tcp] = &dns.Server{Listener: l, Net: "tcp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
		ctx := context.Background()
		s.ServeDNS(ctx, w, r)
	})}
	s.m.Unlock()

	return s.server[tcp].ActivateAndServe()
}

// ServePacket starts the server with an existing packetconn. It blocks until the server stops.
// This implements caddy.UDPServer interface.
func (s *Server) ServePacket(p net.PacketConn) error {
	s.m.Lock()
	s.server[udp] = &dns.Server{PacketConn: p, Net: "udp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
		ctx := context.Background()
		s.ServeDNS(ctx, w, r)
	})}
	s.m.Unlock()

	return s.server[udp].ActivateAndServe()
}

// Listen implements caddy.TCPServer interface.
func (s *Server) Listen() (net.Listener, error) {
	l, err := net.Listen("tcp", s.Addr[len(TransportDNS+"://"):])
	if err != nil {
		return nil, err
	}
	return l, nil
}

// ListenPacket implements caddy.UDPServer interface.
func (s *Server) ListenPacket() (net.PacketConn, error) {
	p, err := net.ListenPacket("udp", s.Addr[len(TransportDNS+"://"):])
	if err != nil {
		return nil, err
	}

	return p, nil
}

// Stop stops the server. It blocks until the server is
// totally stopped. On POSIX systems, it will wait for
// connections to close (up to a max timeout of a few
// seconds); on Windows it will close the listener
// immediately.
// This implements Caddy.Stopper interface.
func (s *Server) Stop() (err error) {

	if runtime.GOOS != "windows" {
		// force connections to close after timeout
		done := make(chan struct{})
		go func() {
			s.dnsWg.Done() // decrement our initial increment used as a barrier
			s.dnsWg.Wait()
			close(done)
		}()

		// Wait for remaining connections to finish or
		// force them all to close after timeout
		select {
		case <-time.After(s.connTimeout):
		case <-done:
		}
	}

	// Close the listener now; this stops the server without delay
	s.m.Lock()
	for _, s1 := range s.server {
		// We might not have started and initialized the full set of servers
		if s1 != nil {
			err = s1.Shutdown()
		}
	}
	s.m.Unlock()
	return
}

// Address together with Stop() implement caddy.GracefulServer.
func (s *Server) Address() string { return s.Addr }

// ServeDNS is the entry point for every request to the address that s
// is bound to. It acts as a multiplexer for the requests zonename as
// defined in the request so that the correct zone
// (configuration and plugin stack) will handle the request.
func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
	// our dns library protects us against really invalid packets, we can still
	// get semi valid packets. Drop them here.
	if r == nil || len(r.Question) == 0 {
		DefaultErrorFunc(w, r, dns.RcodeServerFailure)
		return
	}

	if !s.debug {
		defer func() {
			// In case the user doesn't enable error plugin, we still
			// need to make sure that we stay alive up here
			if rec := recover(); rec != nil {
				DefaultErrorFunc(w, r, dns.RcodeServerFailure)
			}
		}()
	}

	if !s.classChaos && r.Question[0].Qclass != dns.ClassINET {
		DefaultErrorFunc(w, r, dns.RcodeRefused)
		return
	}

	if m, err := edns.Version(r); err != nil { // Wrong EDNS version, return at once.
		w.WriteMsg(m)
		return
	}

	q := r.Question[0].Name
	b := make([]byte, len(q))
	var off int
	var end bool

	var dshandler *Config

	for {
		l := len(q[off:])
		for i := 0; i < l; i++ {
			b[i] = q[off+i]
			// normalize the name for the lookup
			if b[i] >= 'A' && b[i] <= 'Z' {
				b[i] |= ('a' - 'A')
			}
		}

		if h, ok := s.zones[string(b[:l])]; ok {
			if r.Question[0].Qtype != dns.TypeDS {
				if h.FilterFunc == nil {
					rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
					if !plugin.ClientWrite(rcode) {
						DefaultErrorFunc(w, r, rcode)
					}
					return
				}
				// FilterFunc is set, call it to see if we should use this handler.
				// This is given to full query name.
				if h.FilterFunc(q) {
					rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
					if !plugin.ClientWrite(rcode) {
						DefaultErrorFunc(w, r, rcode)
					}
					return
				}
			}
			// The type is DS, keep the handler, but keep on searching as maybe we are serving
			// the parent as well and the DS should be routed to it - this will probably *misroute* DS
			// queries to a possibly grand parent, but there is no way for us to know at this point
			// if there is an actually delegation from grandparent -> parent -> zone.
			// In all fairness: direct DS queries should not be needed.
			dshandler = h
		}
		off, end = dns.NextLabel(q, off)
		if end {
			break
		}
	}

	if r.Question[0].Qtype == dns.TypeDS && dshandler != nil {
		// DS request, and we found a zone, use the handler for the query.
		rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r)
		if !plugin.ClientWrite(rcode) {
			DefaultErrorFunc(w, r, rcode)
		}
		return
	}

	// Wildcard match, if we have found nothing try the root zone as a last resort.
	if h, ok := s.zones["."]; ok {
		rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
		if !plugin.ClientWrite(rcode) {
			DefaultErrorFunc(w, r, rcode)
		}
		return
	}

	// Still here? Error out with REFUSED and some logging
	remoteHost := w.RemoteAddr().String()
	DefaultErrorFunc(w, r, dns.RcodeRefused)
	log.Printf("[INFO] \"%s %s %s\" - No such zone at %s (Remote: %s)", dns.Type(r.Question[0].Qtype), dns.Class(r.Question[0].Qclass), q, s.Addr, remoteHost)
}

// OnStartupComplete lists the sites served by this server
// and any relevant information, assuming Quiet is false.
func (s *Server) OnStartupComplete() {
	if Quiet {
		return
	}

	for zone, config := range s.zones {
		fmt.Println(zone + ":" + config.Port)
	}
}

// Tracer returns the tracer in the server if defined.
func (s *Server) Tracer() ot.Tracer {
	if s.trace == nil {
		return nil
	}

	return s.trace.Tracer()
}

// DefaultErrorFunc responds to an DNS request with an error.
func DefaultErrorFunc(w dns.ResponseWriter, r *dns.Msg, rc int) {
	state := request.Request{W: w, Req: r}

	answer := new(dns.Msg)
	answer.SetRcode(r, rc)

	state.SizeAndDo(answer)

	vars.Report(state, vars.Dropped, rcode.ToString(rc), answer.Len(), time.Now())

	w.WriteMsg(answer)
}

const (
	tcp = 0
	udp = 1
)

// Quiet mode will not show any informative output on initialization.
var Quiet bool
<commit_msg>server: update comment (#1213)<commit_after>// Package dnsserver implements all the interfaces from Caddy, so that CoreDNS can be a servertype plugin.
package dnsserver

import (
	"fmt"
	"log"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/metrics/vars"
	"github.com/coredns/coredns/plugin/pkg/edns"
	"github.com/coredns/coredns/plugin/pkg/rcode"
	"github.com/coredns/coredns/plugin/pkg/trace"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
	ot "github.com/opentracing/opentracing-go"
	"golang.org/x/net/context"
)

// Server represents an instance of a server, which serves
// DNS requests at a particular address (host and port). A
// server is capable of serving numerous zones on
// the same address and the listener may be stopped for
// graceful termination (POSIX only).
type Server struct {
	Addr string // Address we listen on

	server [2]*dns.Server // 0 is a net.Listener, 1 is a net.PacketConn (a *UDPConn) in our case.
	m      sync.Mutex     // protects the servers

	zones       map[string]*Config // zones keyed by their address
	dnsWg       sync.WaitGroup     // used to wait on outstanding connections
	connTimeout time.Duration      // the maximum duration of a graceful shutdown
	trace       trace.Trace        // the trace plugin for the server
	debug       bool               // disable recover()
	classChaos  bool               // allow non-INET class queries
}

// NewServer returns a new CoreDNS server and compiles all plugins in to it. By default CH class
// queries are blocked unless the chaos or proxy is loaded.
func NewServer(addr string, group []*Config) (*Server, error) {

	s := &Server{
		Addr:        addr,
		zones:       make(map[string]*Config),
		connTimeout: 5 * time.Second, // TODO(miek): was configurable
	}

	// We have to bound our wg with one increment
	// to prevent a "race condition" that is hard-coded
	// into sync.WaitGroup.Wait() - basically, an add
	// with a positive delta must be guaranteed to
	// occur before Wait() is called on the wg.
	// In a way, this kind of acts as a safety barrier.
	s.dnsWg.Add(1)

	for _, site := range group {
		if site.Debug {
			s.debug = true
		}
		// set the config per zone
		s.zones[site.Zone] = site
		// compile custom plugin for everything
		var stack plugin.Handler
		for i := len(site.Plugin) - 1; i >= 0; i-- {
			stack = site.Plugin[i](stack)

			// register the *handler* also
			site.registerHandler(stack)

			if s.trace == nil && stack.Name() == "trace" {
				// we have to stash away the plugin, not the
				// Tracer object, because the Tracer won't be initialized yet
				if t, ok := stack.(trace.Trace); ok {
					s.trace = t
				}
			}
			if stack.Name() == "chaos" || stack.Name() == "proxy" {
				s.classChaos = true
			}
		}
		site.pluginChain = stack
	}

	return s, nil
}

// Serve starts the server with an existing listener. It blocks until the server stops.
// This implements caddy.TCPServer interface.
func (s *Server) Serve(l net.Listener) error {
	s.m.Lock()
	s.server[tcp] = &dns.Server{Listener: l, Net: "tcp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
		ctx := context.Background()
		s.ServeDNS(ctx, w, r)
	})}
	s.m.Unlock()

	return s.server[tcp].ActivateAndServe()
}

// ServePacket starts the server with an existing packetconn. It blocks until the server stops.
// This implements caddy.UDPServer interface.
func (s *Server) ServePacket(p net.PacketConn) error {
	s.m.Lock()
	s.server[udp] = &dns.Server{PacketConn: p, Net: "udp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
		ctx := context.Background()
		s.ServeDNS(ctx, w, r)
	})}
	s.m.Unlock()

	return s.server[udp].ActivateAndServe()
}

// Listen implements caddy.TCPServer interface.
func (s *Server) Listen() (net.Listener, error) {
	l, err := net.Listen("tcp", s.Addr[len(TransportDNS+"://"):])
	if err != nil {
		return nil, err
	}
	return l, nil
}

// ListenPacket implements caddy.UDPServer interface.
func (s *Server) ListenPacket() (net.PacketConn, error) {
	p, err := net.ListenPacket("udp", s.Addr[len(TransportDNS+"://"):])
	if err != nil {
		return nil, err
	}

	return p, nil
}

// Stop stops the server. It blocks until the server is
// totally stopped. On POSIX systems, it will wait for
// connections to close (up to a max timeout of a few
// seconds); on Windows it will close the listener
// immediately.
// This implements Caddy.Stopper interface.
func (s *Server) Stop() (err error) {

	if runtime.GOOS != "windows" {
		// force connections to close after timeout
		done := make(chan struct{})
		go func() {
			s.dnsWg.Done() // decrement our initial increment used as a barrier
			s.dnsWg.Wait()
			close(done)
		}()

		// Wait for remaining connections to finish or
		// force them all to close after timeout
		select {
		case <-time.After(s.connTimeout):
		case <-done:
		}
	}

	// Close the listener now; this stops the server without delay
	s.m.Lock()
	for _, s1 := range s.server {
		// We might not have started and initialized the full set of servers
		if s1 != nil {
			err = s1.Shutdown()
		}
	}
	s.m.Unlock()
	return
}

// Address together with Stop() implement caddy.GracefulServer.
func (s *Server) Address() string { return s.Addr }

// ServeDNS is the entry point for every request to the address that s
// is bound to. It acts as a multiplexer for the requests zonename as
// defined in the request so that the correct zone
// (configuration and plugin stack) will handle the request.
func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
	// The default dns.Mux checks the question section size, but we have our
	// own mux here. Check if we have a question section. If not drop them here.
	if r == nil || len(r.Question) == 0 {
		DefaultErrorFunc(w, r, dns.RcodeServerFailure)
		return
	}

	if !s.debug {
		defer func() {
			// In case the user doesn't enable error plugin, we still
			// need to make sure that we stay alive up here
			if rec := recover(); rec != nil {
				DefaultErrorFunc(w, r, dns.RcodeServerFailure)
			}
		}()
	}

	if !s.classChaos && r.Question[0].Qclass != dns.ClassINET {
		DefaultErrorFunc(w, r, dns.RcodeRefused)
		return
	}

	if m, err := edns.Version(r); err != nil { // Wrong EDNS version, return at once.
		w.WriteMsg(m)
		return
	}

	q := r.Question[0].Name
	b := make([]byte, len(q))
	var off int
	var end bool

	var dshandler *Config

	for {
		l := len(q[off:])
		for i := 0; i < l; i++ {
			b[i] = q[off+i]
			// normalize the name for the lookup
			if b[i] >= 'A' && b[i] <= 'Z' {
				b[i] |= ('a' - 'A')
			}
		}

		if h, ok := s.zones[string(b[:l])]; ok {
			if r.Question[0].Qtype != dns.TypeDS {
				if h.FilterFunc == nil {
					rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
					if !plugin.ClientWrite(rcode) {
						DefaultErrorFunc(w, r, rcode)
					}
					return
				}
				// FilterFunc is set, call it to see if we should use this handler.
				// This is given to full query name.
				if h.FilterFunc(q) {
					rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
					if !plugin.ClientWrite(rcode) {
						DefaultErrorFunc(w, r, rcode)
					}
					return
				}
			}
			// The type is DS, keep the handler, but keep on searching as maybe we are serving
			// the parent as well and the DS should be routed to it - this will probably *misroute* DS
			// queries to a possibly grand parent, but there is no way for us to know at this point
			// if there is an actually delegation from grandparent -> parent -> zone.
			// In all fairness: direct DS queries should not be needed.
			dshandler = h
		}
		off, end = dns.NextLabel(q, off)
		if end {
			break
		}
	}

	if r.Question[0].Qtype == dns.TypeDS && dshandler != nil {
		// DS request, and we found a zone, use the handler for the query.
		rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r)
		if !plugin.ClientWrite(rcode) {
			DefaultErrorFunc(w, r, rcode)
		}
		return
	}

	// Wildcard match, if we have found nothing try the root zone as a last resort.
	if h, ok := s.zones["."]; ok {
		rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
		if !plugin.ClientWrite(rcode) {
			DefaultErrorFunc(w, r, rcode)
		}
		return
	}

	// Still here? Error out with REFUSED and some logging
	remoteHost := w.RemoteAddr().String()
	DefaultErrorFunc(w, r, dns.RcodeRefused)
	log.Printf("[INFO] \"%s %s %s\" - No such zone at %s (Remote: %s)", dns.Type(r.Question[0].Qtype), dns.Class(r.Question[0].Qclass), q, s.Addr, remoteHost)
}

// OnStartupComplete lists the sites served by this server
// and any relevant information, assuming Quiet is false.
func (s *Server) OnStartupComplete() {
	if Quiet {
		return
	}

	for zone, config := range s.zones {
		fmt.Println(zone + ":" + config.Port)
	}
}

// Tracer returns the tracer in the server if defined.
func (s *Server) Tracer() ot.Tracer {
	if s.trace == nil {
		return nil
	}

	return s.trace.Tracer()
}

// DefaultErrorFunc responds to an DNS request with an error.
func DefaultErrorFunc(w dns.ResponseWriter, r *dns.Msg, rc int) {
	state := request.Request{W: w, Req: r}

	answer := new(dns.Msg)
	answer.SetRcode(r, rc)

	state.SizeAndDo(answer)

	vars.Report(state, vars.Dropped, rcode.ToString(rc), answer.Len(), time.Now())

	w.WriteMsg(answer)
}

const (
	tcp = 0
	udp = 1
)

// Quiet mode will not show any informative output on initialization.
var Quiet bool
<|endoftext|>


<commit_before>package pkcs7

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"os"
	"testing"
)

//
// Test key and certificate are in testdata/ and were generated with OpenSSL as follows:
//
//   openssl genrsa -out test.key 2048
//   openssl req -nodes -new -key test.key -out test.csr -subj "/C=CA/ST=Ontario/L=Toronto/O=Stefan Arentz/OU=Golang Hacks/CN=example.com"
//   openssl x509 -req -days 1825 -in test.csr -signkey test.key -out test.crt
//

func loadPKCS1PrivateKey(path string) (*rsa.PrivateKey, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid key; no PEM data found")
	}
	if block.Type != "RSA PRIVATE KEY" {
		return nil, errors.New("Invalid key; no RSA PRIVATE KEY block")
	}
	return x509.ParsePKCS1PrivateKey(block.Bytes)
}

func loadCertificate(path string) (*x509.Certificate, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid certificate; no PEM data found")
	}
	if block.Type != "CERTIFICATE" {
		return nil, errors.New("Invalid certificate; no CERTIFICATE block")
	}
	return x509.ParseCertificate(block.Bytes)
}

func Test_Sign(t *testing.T) {
	key, err := loadPKCS1PrivateKey("testdata/test.key")
	if err != nil {
		panic(err)
	}

	cert, err := loadCertificate("testdata/test.crt")
	if err != nil {
		panic(err)
	}

	f, err := os.Open("testdata/test.txt")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	signature, err := pkcs7.Sign(f, cert, key)
	if err != nil {
		t.Error("Cannot Sign:", err)
	}

	if len(signature) == 0 {
		t.Error("Signature is zero length")
	}
}
<commit_msg>Quick fix for tests<commit_after>package pkcs7

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"os"
	"testing"
)

//
// Test key and certificate are in testdata/ and were generated with OpenSSL as follows:
//
//   openssl genrsa -out test.key 2048
//   openssl req -nodes -new -key test.key -out test.csr -subj "/C=CA/ST=Ontario/L=Toronto/O=Stefan Arentz/OU=Golang Hacks/CN=example.com"
//   openssl x509 -req -days 1825 -in test.csr -signkey test.key -out test.crt
//

func loadPKCS1PrivateKey(path string) (*rsa.PrivateKey, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid key; no PEM data found")
	}
	if block.Type != "RSA PRIVATE KEY" {
		return nil, errors.New("Invalid key; no RSA PRIVATE KEY block")
	}
	return x509.ParsePKCS1PrivateKey(block.Bytes)
}

func loadCertificate(path string) (*x509.Certificate, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(bytes)
	if block == nil {
		return nil, errors.New("Invalid certificate; no PEM data found")
	}
	if block.Type != "CERTIFICATE" {
		return nil, errors.New("Invalid certificate; no CERTIFICATE block")
	}
	return x509.ParseCertificate(block.Bytes)
}

func Test_Sign(t *testing.T) {
	key, err := loadPKCS1PrivateKey("testdata/test.key")
	if err != nil {
		panic(err)
	}

	cert, err := loadCertificate("testdata/test.crt")
	if err != nil {
		panic(err)
	}

	f, err := os.Open("testdata/test.txt")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	signature, err := Sign(f, cert, key)
	if err != nil {
		t.Error("Cannot Sign:", err)
	}

	if len(signature) == 0 {
		t.Error("Signature is zero length")
	}
}
<|endoftext|>