loveckiy.ivan
12 months ago
350 changed files with 165666 additions and 0 deletions
@ -0,0 +1,8 @@ |
|||
# Default ignored files |
|||
/shelf/ |
|||
/workspace.xml |
|||
# Editor-based HTTP Client requests |
|||
/httpRequests/ |
|||
# Datasource local storage ignored files |
|||
/dataSources/ |
|||
/dataSources.local.xml |
@ -0,0 +1,9 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<module type="WEB_MODULE" version="4"> |
|||
<component name="Go" enabled="true" /> |
|||
<component name="NewModuleRootManager"> |
|||
<content url="file://$MODULE_DIR$" /> |
|||
<orderEntry type="inheritedJdk" /> |
|||
<orderEntry type="sourceFolder" forTests="false" /> |
|||
</component> |
|||
</module> |
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="ProjectModuleManager"> |
|||
<modules> |
|||
<module fileurl="file://$PROJECT_DIR$/.idea/curl.iml" filepath="$PROJECT_DIR$/.idea/curl.iml" /> |
|||
</modules> |
|||
</component> |
|||
</project> |
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="VcsDirectoryMappings"> |
|||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> |
|||
</component> |
|||
</project> |
@ -0,0 +1,51 @@ |
|||
package curl |
|||
|
|||
import "net/http" |
|||
|
|||
type Builder interface { |
|||
Method(value string) Builder |
|||
Url(value string) Builder |
|||
} |
|||
|
|||
type builder struct { |
|||
method, url, payload string |
|||
response interface{} |
|||
headers map[string]string |
|||
cookies []*http.Cookie |
|||
} |
|||
|
|||
func (b *builder) Method(value string) Builder { |
|||
return &builder{ |
|||
method: value, |
|||
} |
|||
} |
|||
|
|||
func (b *builder) Url(value string) Builder { |
|||
return &builder{ |
|||
url: value, |
|||
} |
|||
} |
|||
|
|||
func (b *builder) Payload(value string) Builder { |
|||
return &builder{ |
|||
payload: value, |
|||
} |
|||
} |
|||
|
|||
func (b *builder) MapToObj(value string) Builder { |
|||
return &builder{ |
|||
payload: value, |
|||
} |
|||
} |
|||
|
|||
func (b *builder) Headers(value string) Builder { |
|||
return &builder{ |
|||
payload: value, |
|||
} |
|||
} |
|||
|
|||
func (b *builder) Cookies(value string) Builder { |
|||
return &builder{ |
|||
payload: value, |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
package curl |
|||
|
|||
import ( |
|||
"crypto/tls" |
|||
"net/http" |
|||
"time" |
|||
) |
|||
|
|||
const shortDuration = 1 * time.Second |
|||
|
|||
type RequestClient struct { |
|||
client http.Client |
|||
builder |
|||
} |
|||
|
|||
var defaultRequestClient RequestClient |
|||
|
|||
// Register регистрируем клиента по-умолчанию
|
|||
func Register(timeout time.Duration) { |
|||
defaultRequestClient = NewClient(timeout) |
|||
return |
|||
} |
|||
|
|||
func NewClient(timeout time.Duration) RequestClient { |
|||
if timeout == 0 { |
|||
timeout = 2 * time.Second |
|||
} |
|||
|
|||
return RequestClient{ |
|||
client: http.Client{ |
|||
Transport: &http.Transport{ |
|||
TLSClientConfig: &tls.Config{ |
|||
InsecureSkipVerify: true, |
|||
}, |
|||
}, |
|||
Timeout: timeout, |
|||
}, |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
module git.lowcodeplatform.net/fabric/packages/curl |
|||
|
|||
go 1.19 |
|||
|
|||
require ( |
|||
git.lowcodeplatform.net/fabric/models v0.1.14 |
|||
github.com/labstack/gommon v0.4.0 |
|||
) |
|||
|
|||
require ( |
|||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect |
|||
github.com/mattn/go-colorable v0.1.11 // indirect |
|||
github.com/mattn/go-isatty v0.0.14 // indirect |
|||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect |
|||
) |
@ -0,0 +1,28 @@ |
|||
git.lowcodeplatform.net/fabric/models v0.1.14 h1:8vdkKEnyh6jm8Iw3eB6t3vrMKcTU9L668eedLu93EXk= |
|||
git.lowcodeplatform.net/fabric/models v0.1.14/go.mod h1:RSR+ysusHS7bhYOCDuWbkuGQkFL0Mum4r/FXPzStUQQ= |
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
|||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= |
|||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= |
|||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= |
|||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= |
|||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= |
|||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= |
|||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= |
|||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= |
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
|||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= |
|||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
|||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= |
|||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= |
|||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= |
|||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
|||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= |
|||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
@ -0,0 +1,143 @@ |
|||
package curl |
|||
|
|||
import ( |
|||
"context" |
|||
"encoding/json" |
|||
"fmt" |
|||
"io/ioutil" |
|||
"net" |
|||
"net/http" |
|||
"net/url" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
// Do всегде возвращает результат в интерфейс + ошибка (полезно для внешних запросов с неизвестной структурой)
|
|||
// сериализуем в объект, при передаче ссылки на переменную типа
|
|||
func (r *RequestClient) Do(ctx context.Context, method, urlc, bodyJSON string, response interface{}, headers map[string]string, cookies []*http.Cookie) (result interface{}, err error) { |
|||
var mapValues map[string]string |
|||
var req *http.Request |
|||
if method == "" { |
|||
method = "POST" |
|||
} |
|||
|
|||
method = strings.Trim(method, " ") |
|||
values := url.Values{} |
|||
actionType := "" |
|||
|
|||
// если в гете мы передали еще и json (его добавляем в строку запроса)
|
|||
// только если в запросе не указаны передаваемые параметры
|
|||
clearUrl := strings.Contains(urlc, "?") |
|||
|
|||
bodyJSON = strings.Replace(bodyJSON, " ", "", -1) |
|||
err = json.Unmarshal([]byte(bodyJSON), &mapValues) |
|||
|
|||
if method == "JSONTOGET" && bodyJSON != "" && clearUrl { |
|||
actionType = "JSONTOGET" |
|||
} |
|||
if method == "JSONTOPOST" && bodyJSON != "" { |
|||
actionType = "JSONTOPOST" |
|||
} |
|||
|
|||
switch actionType { |
|||
case "JSONTOGET": // преобразуем параметры в json в строку запроса
|
|||
if err == nil { |
|||
for k, v := range mapValues { |
|||
values.Set(k, v) |
|||
} |
|||
uri, _ := url.Parse(urlc) |
|||
uri.RawQuery = values.Encode() |
|||
urlc = uri.String() |
|||
req, err = http.NewRequest("GET", urlc, strings.NewReader(bodyJSON)) |
|||
if err != nil { |
|||
return "", fmt.Errorf("error do request, err: %s", err) |
|||
} |
|||
} else { |
|||
fmt.Println("Error! Fail parsed bodyJSON from GET Curl: ", err) |
|||
} |
|||
case "JSONTOPOST": // преобразуем параметры в json в тело запроса
|
|||
if err == nil { |
|||
for k, v := range mapValues { |
|||
values.Set(k, v) |
|||
} |
|||
req, err = http.NewRequest("POST", urlc, strings.NewReader(values.Encode())) |
|||
if err != nil { |
|||
return "", fmt.Errorf("error do request, err: %s", err) |
|||
} |
|||
|
|||
req.PostForm = values |
|||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded") |
|||
} else { |
|||
fmt.Println("Error! Fail parsed bodyJSON to POST: ", err) |
|||
} |
|||
default: |
|||
req, err = http.NewRequest(method, urlc, strings.NewReader(bodyJSON)) |
|||
if err != nil { |
|||
return "", fmt.Errorf("error do request, err: %s", err) |
|||
} |
|||
} |
|||
|
|||
// дополняем переданными заголовками
|
|||
if len(headers) > 0 { |
|||
for k, v := range headers { |
|||
req.Header.Add(k, v) |
|||
} |
|||
} |
|||
|
|||
// дополянем куками назначенными для данного запроса
|
|||
if cookies != nil { |
|||
for _, v := range cookies { |
|||
req.AddCookie(v) |
|||
} |
|||
} |
|||
|
|||
ctx, cancel := context.WithTimeout(req.Context(), shortDuration) |
|||
defer cancel() |
|||
|
|||
endpoint := urlc |
|||
endpoint = strings.Replace(endpoint, "https://", "", 1) |
|||
endpoint = strings.Replace(endpoint, "http://", "", 1) |
|||
endpoint = strings.Replace(endpoint, "/ping", "", 1) |
|||
conn, err := net.Dial("tcp", endpoint) |
|||
if err != nil { |
|||
fmt.Printf("closed url %s, err: %s\n", urlc, err) |
|||
return "", fmt.Errorf("connect is closed", err) |
|||
} |
|||
defer conn.Close() |
|||
|
|||
conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) |
|||
|
|||
req = req.WithContext(ctx) |
|||
client := http.DefaultClient |
|||
req.Close = true |
|||
resp, err := client.Do(req) |
|||
if err != nil { |
|||
return "", fmt.Errorf("error do request, err: %s", err) |
|||
} |
|||
defer resp.Body.Close() |
|||
|
|||
responseData, err := ioutil.ReadAll(resp.Body) |
|||
if err != nil { |
|||
return "", err |
|||
} |
|||
responseString := string(responseData) |
|||
|
|||
// возвращаем объект ответа, если передано - в какой объект класть результат
|
|||
if response != nil { |
|||
json.Unmarshal([]byte(responseString), &response) |
|||
} |
|||
|
|||
// всегда отдаем в интерфейсе результат (полезно, когда внешние запросы или сериализация на клиенте)
|
|||
//json.Unmarshal([]byte(responseString), &result)
|
|||
if resp.StatusCode != 200 { |
|||
err = fmt.Errorf("request is not success. request:%s, status: %s", urlc, resp.Status) |
|||
} |
|||
|
|||
return responseString, err |
|||
} |
|||
|
|||
func (r *RequestClient) Clone() RequestClient { |
|||
return RequestClient{ |
|||
client: r.client, |
|||
} |
|||
} |
@ -0,0 +1,2 @@ |
|||
[url "ssh://git@git.lowcodeplatform.net/"] |
|||
insteadOf = https://git.lowcodeplatform.net/ |
@ -0,0 +1,9 @@ |
|||
.history |
|||
.idea |
|||
.vscode |
|||
.DS_Store |
|||
*~merged* |
|||
*~merged |
|||
/public |
|||
.env |
|||
local |
@ -0,0 +1,3 @@ |
|||
# models |
|||
|
|||
Модели общих сущностей проекта Lowcodeplatform Fabric |
@ -0,0 +1,98 @@ |
|||
package models |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
) |
|||
|
|||
var StatusCode = RStatus{ |
|||
"OK": {"Запрос выполнен", 200, "", nil}, |
|||
"OKLicenseActivation": {"Лицензия была активирована", 200, "", nil}, |
|||
"Unauthorized": {"Ошибка авторизации", 401, "", nil}, |
|||
"NotCache": {"Доступно только в Турбо-режиме", 200, "", nil}, |
|||
"NotStatus": {"Ответ сервера не содержит статус выполнения запроса", 501, "", nil}, |
|||
"NotExtended": {"На сервере отсутствует расширение, которое желает использовать клиент", 501, "", nil}, |
|||
"ErrorFormatJson": {"Ошибка формата JSON-запроса", 500, "ErrorFormatJson", nil}, |
|||
"ErrorTransactionFalse": {"Ошибка выполнения тразакции SQL", 500, "ErrorTransactionFalse", nil}, |
|||
"ErrorBeginDB": {"Ошибка подключения к БД", 500, "ErrorBeginDB", nil}, |
|||
"ErrorPrepareSQL": {"Ошибка подготовки запроса SQL", 500, "ErrorPrepareSQL", nil}, |
|||
"ErrorNullParameter": {"Ошибка! Не передан параметр", 503, "ErrorNullParameter", nil}, |
|||
"ErrorQuery": {"Ошибка запроса на выборку данных", 500, "ErrorQuery", nil}, |
|||
"ErrorScanRows": {"Ошибка переноса данных из запроса в объект", 500, "ErrorScanRows", nil}, |
|||
"ErrorNullFields": {"Не все поля заполнены", 500, "ErrorScanRows", nil}, |
|||
"ErrorAccessType": {"Ошибка доступа к элементу типа", 500, "ErrorAccessType", nil}, |
|||
"ErrorGetData": {"Ошибка доступа данным объекта", 500, "ErrorGetData", nil}, |
|||
"ErrorRevElement": {"Значение было изменено ранее.", 409, "ErrorRevElement", nil}, |
|||
"ErrorForbiddenElement": {"Значение занято другим пользователем.", 403, "ErrorForbiddenElement", nil}, |
|||
"ErrorUnprocessableEntity": {"Необрабатываемый экземпляр", 422, "ErrorUnprocessableEntity", nil}, |
|||
"ErrorNotFound": {"Значение не найдено", 404, "ErrorNotFound", nil}, |
|||
"ErrorReadDir": {"Ошибка чтения директории", 403, "ErrorReadDir", nil}, |
|||
"ErrorReadConfigDir": {"Ошибка чтения директории конфигураций", 403, "ErrorReadConfigDir", nil}, |
|||
"errorOpenConfigDir": {"Ошибка открытия директории конфигураций", 403, "errorOpenConfigDir", nil}, |
|||
"ErrorReadConfigFile": {"Ошибка чтения файла конфигураций", 403, "ErrorReadConfigFile", nil}, |
|||
"ErrorReadLogFile": {"Ошибка чтения файла логирования", 403, "ErrorReadLogFile", nil}, |
|||
"ErrorScanLogFile": {"Ошибка построчного чтения файла логирования", 403, "ErrorScanLogFile", nil}, |
|||
"ErrorPortBusy": {"Указанный порт занят", 403, "ErrorPortBusy", nil}, |
|||
"ErrorGone": {"Объект был удален ранее", 410, "ErrorGone", nil}, |
|||
"ErrorShema": {"Ошибка формата заданной схемы формирования запроса", 410, "ErrorShema", nil}, |
|||
"ErrorInitBase": {"Ошибка инициализации новой базы данных", 410, "ErrorInitBase", nil}, |
|||
"ErrorCreateCacheRecord": {"Ошибка создания объекта в кеше", 410, "ErrorCreateCacheRecord", nil}, |
|||
"ErrorUpdateParams": {"Не переданы параметры для обновления серверов (сервер источник, сервер получатель)", 410, "ErrorUpdateParams", nil}, |
|||
"ErrorIntervalProxy": {"Ошибка переданного интервала (формат: 1000:2000)", 410, "ErrorIntervalProxy", nil}, |
|||
"ErrorReservPortProxy": {"Ошибка выделения порта proxy-сервером", 410, "ErrorReservPortProxy", nil}, |
|||
} |
|||
|
|||
type RStatus map[string]RestStatus |
|||
type RestStatus struct { |
|||
Description string `json:"description"` |
|||
Status int `json:"status"` |
|||
Code string `json:"code"` |
|||
Error error `json:"error"` |
|||
} |
|||
|
|||
func (r RestStatus) MarshalJSON() ([]byte, error) { |
|||
type RestStatusJson struct { |
|||
Description string `json:"description"` |
|||
Status int `json:"status"` |
|||
Code string `json:"code"` |
|||
Error string `json:"error"` |
|||
} |
|||
|
|||
var n = RestStatusJson{} |
|||
n.Description = r.Description |
|||
n.Status = r.Status |
|||
n.Code = r.Code |
|||
n.Error = fmt.Sprint(r.Error) |
|||
if r.Error == nil { |
|||
n.Error = "" |
|||
} |
|||
|
|||
res, err := json.Marshal(n) |
|||
return res, err |
|||
} |
|||
|
|||
func (r RestStatus) UnmarshalJSON(b []byte) error { |
|||
type RestStatusJson struct { |
|||
Description string `json:"description"` |
|||
Status int `json:"status"` |
|||
Code string `json:"code"` |
|||
Error string `json:"error"` |
|||
} |
|||
t := RestStatusJson{} |
|||
|
|||
err := json.Unmarshal(b, &t) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
r.Description = t.Description |
|||
r.Code = t.Code |
|||
r.Status = t.Status |
|||
if t.Error != "" { |
|||
r.Error = nil |
|||
} else { |
|||
r.Error = fmt.Errorf("%s", t.Error) |
|||
} |
|||
|
|||
return nil |
|||
} |
@ -0,0 +1,176 @@ |
|||
package models |
|||
|
|||
import "strings" |
|||
|
|||
type Data struct { |
|||
Uid string `json:"uid"` |
|||
Id string `json:"id"` |
|||
Source string `json:"source"` |
|||
Parent string `json:"parent"` |
|||
Type string `json:"type"` |
|||
Title string `json:"title"` |
|||
Rev string `json:"rev"` |
|||
Сopies string `json:"copies"` |
|||
Attributes map[string]Attribute `json:"attributes"` |
|||
} |
|||
|
|||
type Attribute struct { |
|||
Value string `json:"value"` |
|||
Src string `json:"src"` |
|||
Tpls string `json:"tpls"` |
|||
Status string `json:"status"` |
|||
Rev string `json:"rev"` |
|||
Editor string `json:"editor"` |
|||
} |
|||
|
|||
type Response struct { |
|||
Data interface{} `json:"data"` |
|||
Status RestStatus `json:"status"` |
|||
Metrics Metrics `json:"metrics"` |
|||
} |
|||
|
|||
type ResponseData struct { |
|||
Data []Data `json:"data"` |
|||
Res interface{} `json:"res"` |
|||
Status RestStatus `json:"status"` |
|||
Metrics Metrics `json:"metrics"` |
|||
} |
|||
|
|||
type Metrics struct { |
|||
ResultSize int `json:"result_size"` |
|||
ResultCount int `json:"result_count"` |
|||
ResultOffset int `json:"result_offset"` |
|||
ResultLimit int `json:"result_limit"` |
|||
ResultPage int `json:"result_page"` |
|||
TimeExecution string `json:"time_execution"` |
|||
TimeQuery string `json:"time_query"` |
|||
|
|||
PageLast int `json:"page_last"` |
|||
PageCurrent int `json:"page_current"` |
|||
PageList []int `json:"page_list"` |
|||
PageFrom int `json:"page_from"` |
|||
PageTo int `json:"page_to"` |
|||
} |
|||
|
|||
// возвращаем необходимый значение атрибута для объекта если он есть, инае пусто
|
|||
// а также из заголовка объекта
|
|||
func (p *Data) Attr(name, element string) (result string, found bool) { |
|||
|
|||
if _, found := p.Attributes[name]; found { |
|||
|
|||
// фикс для тех объектов, на которых добавлено скрытое поле Uid
|
|||
if name == "uid" { |
|||
return p.Uid, true |
|||
} |
|||
|
|||
switch element { |
|||
case "src": |
|||
return p.Attributes[name].Src, true |
|||
case "value": |
|||
return p.Attributes[name].Value, true |
|||
case "tpls": |
|||
return p.Attributes[name].Tpls, true |
|||
case "rev": |
|||
return p.Attributes[name].Rev, true |
|||
case "status": |
|||
return p.Attributes[name].Status, true |
|||
case "uid": |
|||
return p.Uid, true |
|||
case "source": |
|||
return p.Source, true |
|||
case "id": |
|||
return p.Id, true |
|||
case "title": |
|||
return p.Title, true |
|||
case "type": |
|||
return p.Type, true |
|||
} |
|||
} else { |
|||
switch name { |
|||
case "uid": |
|||
return p.Uid, true |
|||
case "source": |
|||
return p.Source, true |
|||
case "id": |
|||
return p.Id, true |
|||
case "title": |
|||
return p.Title, true |
|||
case "type": |
|||
return p.Type, true |
|||
} |
|||
} |
|||
return "", false |
|||
} |
|||
|
|||
// заменяем значение аттрибутов в объекте профиля
|
|||
func (p *Data) AttrSet(name, element, value string) bool { |
|||
g := Attribute{} |
|||
|
|||
for k, v := range p.Attributes { |
|||
if k == name { |
|||
g = v |
|||
} |
|||
} |
|||
|
|||
switch element { |
|||
case "src": |
|||
g.Src = value |
|||
case "value": |
|||
g.Value = value |
|||
case "tpls": |
|||
g.Tpls = value |
|||
case "rev": |
|||
g.Rev = value |
|||
case "status": |
|||
g.Status = value |
|||
} |
|||
|
|||
f := p.Attributes |
|||
|
|||
for k, _ := range f { |
|||
if k == name { |
|||
f[k] = g |
|||
return true |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
// удаляем элемент из слайса
|
|||
func (p *ResponseData) RemoveData(i int) bool { |
|||
|
|||
if i < len(p.Data) { |
|||
p.Data = append(p.Data[:i], p.Data[i+1:]...) |
|||
} else { |
|||
//log.Warning("Error! Position invalid (", i, ")")
|
|||
return false |
|||
} |
|||
|
|||
return true |
|||
} |
|||
|
|||
// FilterRole применяем ограничения доступа для объектов типа ResponseData
|
|||
// фильтруем массив данных
|
|||
// если непустое поле access_read, значит назначены права, а следовательно проверяем право просмотра для роли пользователя
|
|||
// также возвращаем
|
|||
func (p *ResponseData) FilterRole(role string) { |
|||
sliceData := p.Data |
|||
|
|||
for i := len(sliceData) - 1; i >= 0; i-- { |
|||
v := sliceData[i] |
|||
attr_read, _ := v.Attr("access_read", "src") |
|||
attr_write, _ := v.Attr("attr_write", "src") |
|||
attr_delete, _ := v.Attr("attr_delete", "src") |
|||
attr_admin, _ := v.Attr("attr_admin", "src") |
|||
|
|||
if (!strings.Contains(attr_read, role) || attr_read == "") && |
|||
(!strings.Contains(attr_write, role) || attr_write == "") && |
|||
(!strings.Contains(attr_delete, role) || attr_delete == "") && |
|||
(!strings.Contains(attr_admin, role) || attr_admin == "") { |
|||
p.RemoveData(i) |
|||
} |
|||
} |
|||
|
|||
return |
|||
} |
@ -0,0 +1,28 @@ |
|||
package models |
|||
|
|||
// Pong тип ответа, который сервис отдает прокси при периодическом опросе (ping-е)
|
|||
type Pong struct { |
|||
Uid string `json:"uid"` |
|||
Config string `json:"config"` |
|||
Name string `json:"name"` |
|||
Version string `json:"version"` |
|||
Status string `json:"status"` |
|||
Host string `json:"host"` |
|||
Pid string `json:"pid"` |
|||
Replicas int `json:"replicas"` |
|||
PortHTTP int `json:"portHTTP"` |
|||
PortGrpc int `json:"portGrpc"` |
|||
PortMetric int `json:"portMetric"` |
|||
PortHTTPS int `json:"portHTTPS"` |
|||
EnableHttps bool `json:"enableHttps"` |
|||
DeadTime int64 `json:"dead_time"` |
|||
Follower string `json:"follower"` |
|||
Metrics interface{} `json:"metrics"` |
|||
} |
|||
|
|||
type Hosts struct { |
|||
Host string `json:"host"` |
|||
PortFrom int `json:"portfrom"` |
|||
PortTo int `json:"portto"` |
|||
Protocol string `json:"protocol"` |
|||
} |
@ -0,0 +1,77 @@ |
|||
package models |
|||
|
|||
type ProfileData struct { |
|||
Revision string `json:"revision"` // ревизия текущей сессии (если сессия обновляется (меняется профиль) - ID-сессии остается, но ревизия меняется
|
|||
Hash string `json:"hash"` |
|||
Email string `json:"email"` |
|||
Uid string `json:"uid"` |
|||
ObjUid string `json:"obj_uid"` |
|||
FirstName string `json:"first_name"` |
|||
LastName string `json:"last_name"` |
|||
Photo string `json:"photo"` |
|||
Age string `json:"age"` |
|||
City string `json:"city"` |
|||
Country string `json:"country"` |
|||
Oauth_identity string `json:"oauth_identity"` |
|||
Status string `json:"status"` // - src поля Status в профиле (иногда необходимо для доп.фильтрации)
|
|||
Raw []Data `json:"raw"` // объект пользователя (нужен при сборки проекта для данного юзера при добавлении прав на базу)
|
|||
Tables []Data `json:"tables"` |
|||
Roles []Data |
|||
Homepage string `json:"homepage"` |
|||
Maket string `json:"maket"` |
|||
UpdateFlag bool `json:"update_flag"` |
|||
UpdateData []Data `json:"update_data"` |
|||
CurrentRole Data `json:"current_role"` |
|||
Profiles []Data `json:"profiles"` |
|||
CurrentProfile Data `json:"current_profile"` |
|||
Navigator []*Items `json:"navigator"` |
|||
|
|||
Groups string |
|||
GroupsValue string |
|||
GroupsDefaultSrc string |
|||
GroupsDefaultValue string |
|||
|
|||
ButtonsNavTop []Data |
|||
CountLicense int |
|||
BaseMode map[string]string |
|||
|
|||
// TODO проверить где используется и выпилить
|
|||
RolesOld map[string]string `json:"roles"` //deplicated
|
|||
First_name string //deplicated
|
|||
Last_name string //deplicated
|
|||
|
|||
} |
|||
|
|||
type Items struct { |
|||
Title string `json:"title"` |
|||
ExtentedLink string `json:"extentedLink"` |
|||
Uid string `json:"uid"` |
|||
Source string `json:"source"` |
|||
Icon string `json:"icon"` |
|||
Leader string `json:"leader"` |
|||
Order string `json:"order"` |
|||
Type string `json:"type"` |
|||
Preview string `json:"preview"` |
|||
Url string `json:"url"` |
|||
Sub []string `json:"sub"` |
|||
Incl []*Items `json:"incl"` |
|||
Class string `json:"class"` |
|||
FinderMode string `json:"finder_mode"` |
|||
} |
|||
|
|||
// ScanSub метод типа Items (перемещаем структуры в карте, исходя из заявленной вложенности элементов)
|
|||
// (переделать дубль фукнции)
|
|||
func (p *Items) ScanSub(maps *map[string]*Items) { |
|||
if p.Sub != nil && len(p.Sub) != 0 { |
|||
for _, c := range p.Sub { |
|||
gg := *maps |
|||
fromP := gg[c] |
|||
if fromP != nil { |
|||
copyPolygon := *fromP |
|||
p.Incl = append(p.Incl, ©Polygon) |
|||
delete(*maps, c) |
|||
copyPolygon.ScanSub(maps) |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
package models |
|||
|
|||
import "github.com/golang-jwt/jwt" |
|||
|
|||
type Token struct { |
|||
Uid string |
|||
Role string |
|||
Profile string |
|||
Groups string |
|||
Local string |
|||
Type string |
|||
Session string |
|||
SessionRev string // ревизия текущей сессии (если сессия обновляется (меняется профиль) - ID-сессии остается, но ревизия меняется
|
|||
jwt.StandardClaims |
|||
} |
|||
|
|||
type Roles struct { |
|||
Title string |
|||
Uid string |
|||
} |
@ -0,0 +1,50 @@ |
|||
package models |
|||
|
|||
type DataTree struct { |
|||
Uid string `json:"uid"` |
|||
Id string `json:"id"` |
|||
Source string `json:"source"` |
|||
Parent string `json:"parent"` |
|||
Type string `json:"type"` |
|||
Title string `json:"title"` |
|||
Rev string `json:"rev"` |
|||
Сopies string `json:"copies"` |
|||
Attributes map[string]Attribute `json:"attributes"` |
|||
Sub []string `json:"sub"` |
|||
Incl []*DataTree `json:"incl"` |
|||
} |
|||
|
|||
type DataTreeOut struct { |
|||
Uid string `json:"uid"` |
|||
Id string `json:"id"` |
|||
Source string `json:"source"` |
|||
Parent string `json:"parent"` |
|||
Type string `json:"type"` |
|||
Title string `json:"title"` |
|||
Rev string `json:"rev"` |
|||
Сopies string `json:"copies"` |
|||
Attributes map[string]Attribute `json:"attributes"` |
|||
Sub []string `json:"sub"` |
|||
Incl []DataTree `json:"incl"` |
|||
} |
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////////
|
|||
////////////////////////////////////////////////////////////////////////////////////////
|
|||
////////////////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
// метод типа Items (перемещаем структуры в карте, исходя из заявленной вложенности элементов)
|
|||
// (переделать дубль фукнции)
|
|||
func (p *DataTree) ScanSub(maps *map[string]*DataTree) { |
|||
if p.Sub != nil && len(p.Sub) != 0 { |
|||
for _, c := range p.Sub { |
|||
gg := *maps |
|||
fromP := gg[c] |
|||
if fromP != nil { |
|||
copyPolygon := *fromP |
|||
p.Incl = append(p.Incl, ©Polygon) |
|||
delete(*maps, c) |
|||
copyPolygon.ScanSub(maps) |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
.DS_Store |
|||
bin |
|||
.idea/ |
|||
|
@ -0,0 +1,9 @@ |
|||
Copyright (c) 2012 Dave Grijalva |
|||
Copyright (c) 2021 golang-jwt maintainers |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|||
|
@ -0,0 +1,22 @@ |
|||
## Migration Guide (v3.2.1) |
|||
|
|||
Starting from [v3.2.1](https://github.com/golang-jwt/jwt/releases/tag/v3.2.1]), the import path has changed from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`. Future releases will be using the `github.com/golang-jwt/jwt` import path and continue the existing versioning scheme of `v3.x.x+incompatible`. Backwards-compatible patches and fixes will be done on the `v3` release branch, where as new build-breaking features will be developed in a `v4` release, possibly including a SIV-style import path. |
|||
|
|||
### go.mod replacement |
|||
|
|||
In a first step, the easiest way is to use `go mod edit` to issue a replacement. |
|||
|
|||
``` |
|||
go mod edit -replace github.com/dgrijalva/jwt-go=github.com/golang-jwt/jwt@v3.2.1+incompatible |
|||
go mod tidy |
|||
``` |
|||
|
|||
This will still keep the old import path in your code but replace it with the new package and also introduce a new indirect dependency to `github.com/golang-jwt/jwt`. Try to compile your project; it should still work. |
|||
|
|||
### Cleanup |
|||
|
|||
If your code still consistently builds, you can replace all occurences of `github.com/dgrijalva/jwt-go` with `github.com/golang-jwt/jwt`, either manually or by using tools such as `sed`. Finally, the `replace` directive in the `go.mod` file can be removed. |
|||
|
|||
## Older releases (before v3.2.0) |
|||
|
|||
The original migration guide for older releases can be found at https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md. |
@ -0,0 +1,113 @@ |
|||
# jwt-go |
|||
|
|||
[![build](https://github.com/golang-jwt/jwt/actions/workflows/build.yml/badge.svg)](https://github.com/golang-jwt/jwt/actions/workflows/build.yml) |
|||
[![Go Reference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt) |
|||
|
|||
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519). |
|||
|
|||
**IMPORT PATH CHANGE:** Starting from [v3.2.1](https://github.com/golang-jwt/jwt/releases/tag/v3.2.1), the import path has changed from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`. After the original author of the library suggested migrating the maintenance of `jwt-go`, a dedicated team of open source maintainers decided to clone the existing library into this repository. See [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a detailed discussion on this topic. |
|||
|
|||
Future releases will be using the `github.com/golang-jwt/jwt` import path and continue the existing versioning scheme of `v3.x.x+incompatible`. Backwards-compatible patches and fixes will be done on the `v3` release branch, where as new build-breaking features will be developed in a `v4` release, possibly including a SIV-style import path. |
|||
|
|||
**SECURITY NOTICE:** Some older versions of Go have a security issue in the crypto/elliptic. Recommendation is to upgrade to at least 1.15 See issue [dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more detail. |
|||
|
|||
**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided. |
|||
|
|||
### Supported Go versions |
|||
|
|||
Our support of Go versions is aligned with Go's [version release policy](https://golang.org/doc/devel/release#policy). |
|||
So we will support a major version of Go until there are two newer major releases. |
|||
We no longer support building jwt-go with unsupported Go versions, as these contain security vulnerabilities |
|||
which will not be fixed. |
|||
|
|||
## What the heck is a JWT? |
|||
|
|||
JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens. |
|||
|
|||
In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](https://datatracker.ietf.org/doc/html/rfc4648) encoded. The last part is the signature, encoded the same way. |
|||
|
|||
The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. |
|||
|
|||
The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) for information about reserved keys and the proper way to add your own. |
|||
|
|||
## What's in the box? |
|||
|
|||
This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. |
|||
|
|||
## Examples |
|||
|
|||
See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt) for examples of usage: |
|||
|
|||
* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-Parse-Hmac) |
|||
* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-New-Hmac) |
|||
* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt#pkg-examples) |
|||
|
|||
## Extensions |
|||
|
|||
This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. |
|||
|
|||
Here's an example of an extension that integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS): https://github.com/someone1/gcp-jwt-go |
|||
|
|||
## Compliance |
|||
|
|||
This library was last reviewed to comply with [RTF 7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few notable differences: |
|||
|
|||
* In order to protect against accidental use of [Unsecured JWTs](https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key. |
|||
|
|||
## Project Status & Versioning |
|||
|
|||
This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). |
|||
|
|||
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `main`. Periodically, versions will be tagged from `main`. You can find all the releases on [the project releases page](https://github.com/golang-jwt/jwt/releases). |
|||
|
|||
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/golang-jwt/jwt.v3`. It will do the right thing WRT semantic versioning. |
|||
|
|||
**BREAKING CHANGES:*** |
|||
* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. |
|||
|
|||
## Usage Tips |
|||
|
|||
### Signing vs Encryption |
|||
|
|||
A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data: |
|||
|
|||
* The author of the token was in the possession of the signing secret |
|||
* The data has not been modified since it was signed |
|||
|
|||
It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. |
|||
|
|||
### Choosing a Signing Method |
|||
|
|||
There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric. |
|||
|
|||
Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation. |
|||
|
|||
Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. |
|||
|
|||
### Signing Methods and Key Types |
|||
|
|||
Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones: |
|||
|
|||
* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation |
|||
* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation |
|||
* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation |
|||
|
|||
### JWT and OAuth |
|||
|
|||
It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. |
|||
|
|||
Without going too far down the rabbit hole, here's a description of the interaction of these technologies: |
|||
|
|||
* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth. |
|||
* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. |
|||
* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL. |
|||
|
|||
### Troubleshooting |
|||
|
|||
This library uses descriptive error messages whenever possible. If you are not getting the expected result, have a look at the errors. The most common place people get stuck is providing the correct type of key to the parser. See the above section on signing methods and key types. |
|||
|
|||
## More |
|||
|
|||
Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt). |
|||
|
|||
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation. |
@ -0,0 +1,131 @@ |
|||
## `jwt-go` Version History |
|||
|
|||
#### 3.2.2 |
|||
|
|||
* Starting from this release, we are adopting the policy to support the most 2 recent versions of Go currently available. By the time of this release, this is Go 1.15 and 1.16 ([#28](https://github.com/golang-jwt/jwt/pull/28)). |
|||
* Fixed a potential issue that could occur when the verification of `exp`, `iat` or `nbf` was not required and contained invalid contents, i.e. non-numeric/date. Thanks for @thaJeztah for making us aware of that and @giorgos-f3 for originally reporting it to the formtech fork ([#40](https://github.com/golang-jwt/jwt/pull/40)). |
|||
* Added support for EdDSA / ED25519 ([#36](https://github.com/golang-jwt/jwt/pull/36)). |
|||
* Optimized allocations ([#33](https://github.com/golang-jwt/jwt/pull/33)). |
|||
|
|||
#### 3.2.1 |
|||
|
|||
* **Import Path Change**: See MIGRATION_GUIDE.md for tips on updating your code |
|||
* Changed the import path from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt` |
|||
* Fixed type confusing issue between `string` and `[]string` in `VerifyAudience` ([#12](https://github.com/golang-jwt/jwt/pull/12)). This fixes CVE-2020-26160 |
|||
|
|||
#### 3.2.0 |
|||
|
|||
* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation |
|||
* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate |
|||
* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before. |
|||
* Deprecated `ParseFromRequestWithClaims` to simplify API in the future. |
|||
|
|||
#### 3.1.0 |
|||
|
|||
* Improvements to `jwt` command line tool |
|||
* Added `SkipClaimsValidation` option to `Parser` |
|||
* Documentation updates |
|||
|
|||
#### 3.0.0 |
|||
|
|||
* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code |
|||
* Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods. |
|||
* `ParseFromRequest` has been moved to `request` subpackage and usage has changed |
|||
* The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims. |
|||
* Other Additions and Changes |
|||
* Added `Claims` interface type to allow users to decode the claims into a custom type |
|||
* Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into. |
|||
* Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage |
|||
* Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims` |
|||
* Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`. |
|||
* Added several new, more specific, validation errors to error type bitmask |
|||
* Moved examples from README to executable example files |
|||
* Signing method registry is now thread safe |
|||
* Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser) |
|||
|
|||
#### 2.7.0 |
|||
|
|||
This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes. |
|||
|
|||
* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying |
|||
* Error text for expired tokens includes how long it's been expired |
|||
* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM` |
|||
* Documentation updates |
|||
|
|||
#### 2.6.0 |
|||
|
|||
* Exposed inner error within ValidationError |
|||
* Fixed validation errors when using UseJSONNumber flag |
|||
* Added several unit tests |
|||
|
|||
#### 2.5.0 |
|||
|
|||
* Added support for signing method none. You shouldn't use this. The API tries to make this clear. |
|||
* Updated/fixed some documentation |
|||
* Added more helpful error message when trying to parse tokens that begin with `BEARER ` |
|||
|
|||
#### 2.4.0 |
|||
|
|||
* Added new type, Parser, to allow for configuration of various parsing parameters |
|||
* You can now specify a list of valid signing methods. Anything outside this set will be rejected. |
|||
* You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON |
|||
* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go) |
|||
* Fixed some bugs with ECDSA parsing |
|||
|
|||
#### 2.3.0 |
|||
|
|||
* Added support for ECDSA signing methods |
|||
* Added support for RSA PSS signing methods (requires go v1.4) |
|||
|
|||
#### 2.2.0 |
|||
|
|||
* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic. |
|||
|
|||
#### 2.1.0 |
|||
|
|||
Backwards compatible API change that was missed in 2.0.0. |
|||
|
|||
* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte` |
|||
|
|||
#### 2.0.0 |
|||
|
|||
There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. |
|||
|
|||
The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. |
|||
|
|||
It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. |
|||
|
|||
* **Compatibility Breaking Changes** |
|||
* `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct` |
|||
* `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct` |
|||
* `KeyFunc` now returns `interface{}` instead of `[]byte` |
|||
* `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key |
|||
* `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key |
|||
* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type. |
|||
* Added public package global `SigningMethodHS256` |
|||
* Added public package global `SigningMethodHS384` |
|||
* Added public package global `SigningMethodHS512` |
|||
* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type. |
|||
* Added public package global `SigningMethodRS256` |
|||
* Added public package global `SigningMethodRS384` |
|||
* Added public package global `SigningMethodRS512` |
|||
* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged. |
|||
* Refactored the RSA implementation to be easier to read |
|||
* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM` |
|||
|
|||
#### 1.0.2 |
|||
|
|||
* Fixed bug in parsing public keys from certificates |
|||
* Added more tests around the parsing of keys for RS256 |
|||
* Code refactoring in RS256 implementation. No functional changes |
|||
|
|||
#### 1.0.1 |
|||
|
|||
* Fixed panic if RS256 signing method was passed an invalid key |
|||
|
|||
#### 1.0.0 |
|||
|
|||
* First versioned release |
|||
* API stabilized |
|||
* Supports creating, signing, parsing, and validating JWT tokens |
|||
* Supports RS256 and HS256 signing methods |
@ -0,0 +1,146 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto/subtle" |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// For a type to be a Claims object, it must just have a Valid method that determines
|
|||
// if the token is invalid for any supported reason
|
|||
type Claims interface { |
|||
Valid() error |
|||
} |
|||
|
|||
// Structured version of Claims Section, as referenced at
|
|||
// https://tools.ietf.org/html/rfc7519#section-4.1
|
|||
// See examples for how to use this with your own claim types
|
|||
type StandardClaims struct { |
|||
Audience string `json:"aud,omitempty"` |
|||
ExpiresAt int64 `json:"exp,omitempty"` |
|||
Id string `json:"jti,omitempty"` |
|||
IssuedAt int64 `json:"iat,omitempty"` |
|||
Issuer string `json:"iss,omitempty"` |
|||
NotBefore int64 `json:"nbf,omitempty"` |
|||
Subject string `json:"sub,omitempty"` |
|||
} |
|||
|
|||
// Validates time based claims "exp, iat, nbf".
|
|||
// There is no accounting for clock skew.
|
|||
// As well, if any of the above claims are not in the token, it will still
|
|||
// be considered a valid claim.
|
|||
func (c StandardClaims) Valid() error { |
|||
vErr := new(ValidationError) |
|||
now := TimeFunc().Unix() |
|||
|
|||
// The claims below are optional, by default, so if they are set to the
|
|||
// default value in Go, let's not fail the verification for them.
|
|||
if !c.VerifyExpiresAt(now, false) { |
|||
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) |
|||
vErr.Inner = fmt.Errorf("token is expired by %v", delta) |
|||
vErr.Errors |= ValidationErrorExpired |
|||
} |
|||
|
|||
if !c.VerifyIssuedAt(now, false) { |
|||
vErr.Inner = fmt.Errorf("Token used before issued") |
|||
vErr.Errors |= ValidationErrorIssuedAt |
|||
} |
|||
|
|||
if !c.VerifyNotBefore(now, false) { |
|||
vErr.Inner = fmt.Errorf("token is not valid yet") |
|||
vErr.Errors |= ValidationErrorNotValidYet |
|||
} |
|||
|
|||
if vErr.valid() { |
|||
return nil |
|||
} |
|||
|
|||
return vErr |
|||
} |
|||
|
|||
// Compares the aud claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { |
|||
return verifyAud([]string{c.Audience}, cmp, req) |
|||
} |
|||
|
|||
// Compares the exp claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { |
|||
return verifyExp(c.ExpiresAt, cmp, req) |
|||
} |
|||
|
|||
// Compares the iat claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { |
|||
return verifyIat(c.IssuedAt, cmp, req) |
|||
} |
|||
|
|||
// Compares the iss claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { |
|||
return verifyIss(c.Issuer, cmp, req) |
|||
} |
|||
|
|||
// Compares the nbf claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { |
|||
return verifyNbf(c.NotBefore, cmp, req) |
|||
} |
|||
|
|||
// ----- helpers
|
|||
|
|||
func verifyAud(aud []string, cmp string, required bool) bool { |
|||
if len(aud) == 0 { |
|||
return !required |
|||
} |
|||
// use a var here to keep constant time compare when looping over a number of claims
|
|||
result := false |
|||
|
|||
var stringClaims string |
|||
for _, a := range aud { |
|||
if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 { |
|||
result = true |
|||
} |
|||
stringClaims = stringClaims + a |
|||
} |
|||
|
|||
// case where "" is sent in one or many aud claims
|
|||
if len(stringClaims) == 0 { |
|||
return !required |
|||
} |
|||
|
|||
return result |
|||
} |
|||
|
|||
func verifyExp(exp int64, now int64, required bool) bool { |
|||
if exp == 0 { |
|||
return !required |
|||
} |
|||
return now <= exp |
|||
} |
|||
|
|||
func verifyIat(iat int64, now int64, required bool) bool { |
|||
if iat == 0 { |
|||
return !required |
|||
} |
|||
return now >= iat |
|||
} |
|||
|
|||
func verifyIss(iss string, cmp string, required bool) bool { |
|||
if iss == "" { |
|||
return !required |
|||
} |
|||
if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
func verifyNbf(nbf int64, now int64, required bool) bool { |
|||
if nbf == 0 { |
|||
return !required |
|||
} |
|||
return now >= nbf |
|||
} |
@ -0,0 +1,4 @@ |
|||
// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html
|
|||
//
|
|||
// See README.md for more info.
|
|||
package jwt |
@ -0,0 +1,142 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto" |
|||
"crypto/ecdsa" |
|||
"crypto/rand" |
|||
"errors" |
|||
"math/big" |
|||
) |
|||
|
|||
var ( |
|||
// Sadly this is missing from crypto/ecdsa compared to crypto/rsa
|
|||
ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") |
|||
) |
|||
|
|||
// Implements the ECDSA family of signing methods signing methods
|
|||
// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification
|
|||
type SigningMethodECDSA struct { |
|||
Name string |
|||
Hash crypto.Hash |
|||
KeySize int |
|||
CurveBits int |
|||
} |
|||
|
|||
// Specific instances for EC256 and company
|
|||
var ( |
|||
SigningMethodES256 *SigningMethodECDSA |
|||
SigningMethodES384 *SigningMethodECDSA |
|||
SigningMethodES512 *SigningMethodECDSA |
|||
) |
|||
|
|||
func init() { |
|||
// ES256
|
|||
SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} |
|||
RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { |
|||
return SigningMethodES256 |
|||
}) |
|||
|
|||
// ES384
|
|||
SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} |
|||
RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { |
|||
return SigningMethodES384 |
|||
}) |
|||
|
|||
// ES512
|
|||
SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} |
|||
RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { |
|||
return SigningMethodES512 |
|||
}) |
|||
} |
|||
|
|||
func (m *SigningMethodECDSA) Alg() string { |
|||
return m.Name |
|||
} |
|||
|
|||
// Implements the Verify method from SigningMethod
|
|||
// For this verify method, key must be an ecdsa.PublicKey struct
|
|||
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { |
|||
var err error |
|||
|
|||
// Decode the signature
|
|||
var sig []byte |
|||
if sig, err = DecodeSegment(signature); err != nil { |
|||
return err |
|||
} |
|||
|
|||
// Get the key
|
|||
var ecdsaKey *ecdsa.PublicKey |
|||
switch k := key.(type) { |
|||
case *ecdsa.PublicKey: |
|||
ecdsaKey = k |
|||
default: |
|||
return ErrInvalidKeyType |
|||
} |
|||
|
|||
if len(sig) != 2*m.KeySize { |
|||
return ErrECDSAVerification |
|||
} |
|||
|
|||
r := big.NewInt(0).SetBytes(sig[:m.KeySize]) |
|||
s := big.NewInt(0).SetBytes(sig[m.KeySize:]) |
|||
|
|||
// Create hasher
|
|||
if !m.Hash.Available() { |
|||
return ErrHashUnavailable |
|||
} |
|||
hasher := m.Hash.New() |
|||
hasher.Write([]byte(signingString)) |
|||
|
|||
// Verify the signature
|
|||
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus { |
|||
return nil |
|||
} |
|||
|
|||
return ErrECDSAVerification |
|||
} |
|||
|
|||
// Implements the Sign method from SigningMethod
|
|||
// For this signing method, key must be an ecdsa.PrivateKey struct
|
|||
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { |
|||
// Get the key
|
|||
var ecdsaKey *ecdsa.PrivateKey |
|||
switch k := key.(type) { |
|||
case *ecdsa.PrivateKey: |
|||
ecdsaKey = k |
|||
default: |
|||
return "", ErrInvalidKeyType |
|||
} |
|||
|
|||
// Create the hasher
|
|||
if !m.Hash.Available() { |
|||
return "", ErrHashUnavailable |
|||
} |
|||
|
|||
hasher := m.Hash.New() |
|||
hasher.Write([]byte(signingString)) |
|||
|
|||
// Sign the string and return r, s
|
|||
if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { |
|||
curveBits := ecdsaKey.Curve.Params().BitSize |
|||
|
|||
if m.CurveBits != curveBits { |
|||
return "", ErrInvalidKey |
|||
} |
|||
|
|||
keyBytes := curveBits / 8 |
|||
if curveBits%8 > 0 { |
|||
keyBytes += 1 |
|||
} |
|||
|
|||
// We serialize the outputs (r and s) into big-endian byte arrays
|
|||
// padded with zeros on the left to make sure the sizes work out.
|
|||
// Output must be 2*keyBytes long.
|
|||
out := make([]byte, 2*keyBytes) |
|||
r.FillBytes(out[0:keyBytes]) // r is assigned to the first half of output.
|
|||
s.FillBytes(out[keyBytes:]) // s is assigned to the second half of output.
|
|||
|
|||
return EncodeSegment(out), nil |
|||
} else { |
|||
return "", err |
|||
} |
|||
} |
@ -0,0 +1,69 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto/ecdsa" |
|||
"crypto/x509" |
|||
"encoding/pem" |
|||
"errors" |
|||
) |
|||
|
|||
var ( |
|||
ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key") |
|||
ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") |
|||
) |
|||
|
|||
// Parse PEM encoded Elliptic Curve Private Key Structure
|
|||
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { |
|||
var err error |
|||
|
|||
// Parse PEM block
|
|||
var block *pem.Block |
|||
if block, _ = pem.Decode(key); block == nil { |
|||
return nil, ErrKeyMustBePEMEncoded |
|||
} |
|||
|
|||
// Parse the key
|
|||
var parsedKey interface{} |
|||
if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { |
|||
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
var pkey *ecdsa.PrivateKey |
|||
var ok bool |
|||
if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { |
|||
return nil, ErrNotECPrivateKey |
|||
} |
|||
|
|||
return pkey, nil |
|||
} |
|||
|
|||
// Parse PEM encoded PKCS1 or PKCS8 public key
|
|||
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { |
|||
var err error |
|||
|
|||
// Parse PEM block
|
|||
var block *pem.Block |
|||
if block, _ = pem.Decode(key); block == nil { |
|||
return nil, ErrKeyMustBePEMEncoded |
|||
} |
|||
|
|||
// Parse the key
|
|||
var parsedKey interface{} |
|||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { |
|||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil { |
|||
parsedKey = cert.PublicKey |
|||
} else { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
var pkey *ecdsa.PublicKey |
|||
var ok bool |
|||
if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { |
|||
return nil, ErrNotECPublicKey |
|||
} |
|||
|
|||
return pkey, nil |
|||
} |
@ -0,0 +1,81 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"errors" |
|||
|
|||
"crypto/ed25519" |
|||
) |
|||
|
|||
var ( |
|||
ErrEd25519Verification = errors.New("ed25519: verification error") |
|||
) |
|||
|
|||
// Implements the EdDSA family
|
|||
// Expects ed25519.PrivateKey for signing and ed25519.PublicKey for verification
|
|||
type SigningMethodEd25519 struct{} |
|||
|
|||
// Specific instance for EdDSA
|
|||
var ( |
|||
SigningMethodEdDSA *SigningMethodEd25519 |
|||
) |
|||
|
|||
func init() { |
|||
SigningMethodEdDSA = &SigningMethodEd25519{} |
|||
RegisterSigningMethod(SigningMethodEdDSA.Alg(), func() SigningMethod { |
|||
return SigningMethodEdDSA |
|||
}) |
|||
} |
|||
|
|||
func (m *SigningMethodEd25519) Alg() string { |
|||
return "EdDSA" |
|||
} |
|||
|
|||
// Implements the Verify method from SigningMethod
|
|||
// For this verify method, key must be an ed25519.PublicKey
|
|||
func (m *SigningMethodEd25519) Verify(signingString, signature string, key interface{}) error { |
|||
var err error |
|||
var ed25519Key ed25519.PublicKey |
|||
var ok bool |
|||
|
|||
if ed25519Key, ok = key.(ed25519.PublicKey); !ok { |
|||
return ErrInvalidKeyType |
|||
} |
|||
|
|||
if len(ed25519Key) != ed25519.PublicKeySize { |
|||
return ErrInvalidKey |
|||
} |
|||
|
|||
// Decode the signature
|
|||
var sig []byte |
|||
if sig, err = DecodeSegment(signature); err != nil { |
|||
return err |
|||
} |
|||
|
|||
// Verify the signature
|
|||
if !ed25519.Verify(ed25519Key, []byte(signingString), sig) { |
|||
return ErrEd25519Verification |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// Implements the Sign method from SigningMethod
|
|||
// For this signing method, key must be an ed25519.PrivateKey
|
|||
func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) (string, error) { |
|||
var ed25519Key ed25519.PrivateKey |
|||
var ok bool |
|||
|
|||
if ed25519Key, ok = key.(ed25519.PrivateKey); !ok { |
|||
return "", ErrInvalidKeyType |
|||
} |
|||
|
|||
// ed25519.Sign panics if private key not equal to ed25519.PrivateKeySize
|
|||
// this allows to avoid recover usage
|
|||
if len(ed25519Key) != ed25519.PrivateKeySize { |
|||
return "", ErrInvalidKey |
|||
} |
|||
|
|||
// Sign the string and return the encoded result
|
|||
sig := ed25519.Sign(ed25519Key, []byte(signingString)) |
|||
return EncodeSegment(sig), nil |
|||
} |
@ -0,0 +1,64 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto" |
|||
"crypto/ed25519" |
|||
"crypto/x509" |
|||
"encoding/pem" |
|||
"errors" |
|||
) |
|||
|
|||
var ( |
|||
ErrNotEdPrivateKey = errors.New("Key is not a valid Ed25519 private key") |
|||
ErrNotEdPublicKey = errors.New("Key is not a valid Ed25519 public key") |
|||
) |
|||
|
|||
// Parse PEM-encoded Edwards curve private key
|
|||
func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { |
|||
var err error |
|||
|
|||
// Parse PEM block
|
|||
var block *pem.Block |
|||
if block, _ = pem.Decode(key); block == nil { |
|||
return nil, ErrKeyMustBePEMEncoded |
|||
} |
|||
|
|||
// Parse the key
|
|||
var parsedKey interface{} |
|||
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
var pkey ed25519.PrivateKey |
|||
var ok bool |
|||
if pkey, ok = parsedKey.(ed25519.PrivateKey); !ok { |
|||
return nil, ErrNotEdPrivateKey |
|||
} |
|||
|
|||
return pkey, nil |
|||
} |
|||
|
|||
// Parse PEM-encoded Edwards curve public key
|
|||
func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { |
|||
var err error |
|||
|
|||
// Parse PEM block
|
|||
var block *pem.Block |
|||
if block, _ = pem.Decode(key); block == nil { |
|||
return nil, ErrKeyMustBePEMEncoded |
|||
} |
|||
|
|||
// Parse the key
|
|||
var parsedKey interface{} |
|||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
var pkey ed25519.PublicKey |
|||
var ok bool |
|||
if pkey, ok = parsedKey.(ed25519.PublicKey); !ok { |
|||
return nil, ErrNotEdPublicKey |
|||
} |
|||
|
|||
return pkey, nil |
|||
} |
@ -0,0 +1,59 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"errors" |
|||
) |
|||
|
|||
// Error constants
|
|||
var ( |
|||
ErrInvalidKey = errors.New("key is invalid") |
|||
ErrInvalidKeyType = errors.New("key is of invalid type") |
|||
ErrHashUnavailable = errors.New("the requested hash function is unavailable") |
|||
) |
|||
|
|||
// The errors that might occur when parsing and validating a token
|
|||
const ( |
|||
ValidationErrorMalformed uint32 = 1 << iota // Token is malformed
|
|||
ValidationErrorUnverifiable // Token could not be verified because of signing problems
|
|||
ValidationErrorSignatureInvalid // Signature validation failed
|
|||
|
|||
// Standard Claim validation errors
|
|||
ValidationErrorAudience // AUD validation failed
|
|||
ValidationErrorExpired // EXP validation failed
|
|||
ValidationErrorIssuedAt // IAT validation failed
|
|||
ValidationErrorIssuer // ISS validation failed
|
|||
ValidationErrorNotValidYet // NBF validation failed
|
|||
ValidationErrorId // JTI validation failed
|
|||
ValidationErrorClaimsInvalid // Generic claims validation error
|
|||
) |
|||
|
|||
// Helper for constructing a ValidationError with a string error message
|
|||
func NewValidationError(errorText string, errorFlags uint32) *ValidationError { |
|||
return &ValidationError{ |
|||
text: errorText, |
|||
Errors: errorFlags, |
|||
} |
|||
} |
|||
|
|||
// The error from Parse if token is not valid
|
|||
type ValidationError struct { |
|||
Inner error // stores the error returned by external dependencies, i.e.: KeyFunc
|
|||
Errors uint32 // bitfield. see ValidationError... constants
|
|||
text string // errors that do not have a valid error just have text
|
|||
} |
|||
|
|||
// Validation error is an error type
|
|||
func (e ValidationError) Error() string { |
|||
if e.Inner != nil { |
|||
return e.Inner.Error() |
|||
} else if e.text != "" { |
|||
return e.text |
|||
} else { |
|||
return "token is invalid" |
|||
} |
|||
} |
|||
|
|||
// No errors
|
|||
func (e *ValidationError) valid() bool { |
|||
return e.Errors == 0 |
|||
} |
@ -0,0 +1,95 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto" |
|||
"crypto/hmac" |
|||
"errors" |
|||
) |
|||
|
|||
// Implements the HMAC-SHA family of signing methods signing methods
|
|||
// Expects key type of []byte for both signing and validation
|
|||
type SigningMethodHMAC struct { |
|||
Name string |
|||
Hash crypto.Hash |
|||
} |
|||
|
|||
// Specific instances for HS256 and company
|
|||
var ( |
|||
SigningMethodHS256 *SigningMethodHMAC |
|||
SigningMethodHS384 *SigningMethodHMAC |
|||
SigningMethodHS512 *SigningMethodHMAC |
|||
ErrSignatureInvalid = errors.New("signature is invalid") |
|||
) |
|||
|
|||
func init() { |
|||
// HS256
|
|||
SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256} |
|||
RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod { |
|||
return SigningMethodHS256 |
|||
}) |
|||
|
|||
// HS384
|
|||
SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384} |
|||
RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod { |
|||
return SigningMethodHS384 |
|||
}) |
|||
|
|||
// HS512
|
|||
SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512} |
|||
RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod { |
|||
return SigningMethodHS512 |
|||
}) |
|||
} |
|||
|
|||
func (m *SigningMethodHMAC) Alg() string { |
|||
return m.Name |
|||
} |
|||
|
|||
// Verify the signature of HSXXX tokens. Returns nil if the signature is valid.
|
|||
func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { |
|||
// Verify the key is the right type
|
|||
keyBytes, ok := key.([]byte) |
|||
if !ok { |
|||
return ErrInvalidKeyType |
|||
} |
|||
|
|||
// Decode signature, for comparison
|
|||
sig, err := DecodeSegment(signature) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
// Can we use the specified hashing method?
|
|||
if !m.Hash.Available() { |
|||
return ErrHashUnavailable |
|||
} |
|||
|
|||
// This signing method is symmetric, so we validate the signature
|
|||
// by reproducing the signature from the signing string and key, then
|
|||
// comparing that against the provided signature.
|
|||
hasher := hmac.New(m.Hash.New, keyBytes) |
|||
hasher.Write([]byte(signingString)) |
|||
if !hmac.Equal(sig, hasher.Sum(nil)) { |
|||
return ErrSignatureInvalid |
|||
} |
|||
|
|||
// No validation errors. Signature is good.
|
|||
return nil |
|||
} |
|||
|
|||
// Implements the Sign method from SigningMethod for this signing method.
|
|||
// Key must be []byte
|
|||
func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { |
|||
if keyBytes, ok := key.([]byte); ok { |
|||
if !m.Hash.Available() { |
|||
return "", ErrHashUnavailable |
|||
} |
|||
|
|||
hasher := hmac.New(m.Hash.New, keyBytes) |
|||
hasher.Write([]byte(signingString)) |
|||
|
|||
return EncodeSegment(hasher.Sum(nil)), nil |
|||
} |
|||
|
|||
return "", ErrInvalidKeyType |
|||
} |
@ -0,0 +1,120 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"errors" |
|||
// "fmt"
|
|||
) |
|||
|
|||
// Claims type that uses the map[string]interface{} for JSON decoding
|
|||
// This is the default claims type if you don't supply one
|
|||
type MapClaims map[string]interface{} |
|||
|
|||
// VerifyAudience Compares the aud claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (m MapClaims) VerifyAudience(cmp string, req bool) bool { |
|||
var aud []string |
|||
switch v := m["aud"].(type) { |
|||
case string: |
|||
aud = append(aud, v) |
|||
case []string: |
|||
aud = v |
|||
case []interface{}: |
|||
for _, a := range v { |
|||
vs, ok := a.(string) |
|||
if !ok { |
|||
return false |
|||
} |
|||
aud = append(aud, vs) |
|||
} |
|||
} |
|||
return verifyAud(aud, cmp, req) |
|||
} |
|||
|
|||
// Compares the exp claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { |
|||
exp, ok := m["exp"] |
|||
if !ok { |
|||
return !req |
|||
} |
|||
switch expType := exp.(type) { |
|||
case float64: |
|||
return verifyExp(int64(expType), cmp, req) |
|||
case json.Number: |
|||
v, _ := expType.Int64() |
|||
return verifyExp(v, cmp, req) |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// Compares the iat claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { |
|||
iat, ok := m["iat"] |
|||
if !ok { |
|||
return !req |
|||
} |
|||
switch iatType := iat.(type) { |
|||
case float64: |
|||
return verifyIat(int64(iatType), cmp, req) |
|||
case json.Number: |
|||
v, _ := iatType.Int64() |
|||
return verifyIat(v, cmp, req) |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// Compares the iss claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (m MapClaims) VerifyIssuer(cmp string, req bool) bool { |
|||
iss, _ := m["iss"].(string) |
|||
return verifyIss(iss, cmp, req) |
|||
} |
|||
|
|||
// Compares the nbf claim against cmp.
|
|||
// If required is false, this method will return true if the value matches or is unset
|
|||
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { |
|||
nbf, ok := m["nbf"] |
|||
if !ok { |
|||
return !req |
|||
} |
|||
switch nbfType := nbf.(type) { |
|||
case float64: |
|||
return verifyNbf(int64(nbfType), cmp, req) |
|||
case json.Number: |
|||
v, _ := nbfType.Int64() |
|||
return verifyNbf(v, cmp, req) |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// Validates time based claims "exp, iat, nbf".
|
|||
// There is no accounting for clock skew.
|
|||
// As well, if any of the above claims are not in the token, it will still
|
|||
// be considered a valid claim.
|
|||
func (m MapClaims) Valid() error { |
|||
vErr := new(ValidationError) |
|||
now := TimeFunc().Unix() |
|||
|
|||
if !m.VerifyExpiresAt(now, false) { |
|||
vErr.Inner = errors.New("Token is expired") |
|||
vErr.Errors |= ValidationErrorExpired |
|||
} |
|||
|
|||
if !m.VerifyIssuedAt(now, false) { |
|||
vErr.Inner = errors.New("Token used before issued") |
|||
vErr.Errors |= ValidationErrorIssuedAt |
|||
} |
|||
|
|||
if !m.VerifyNotBefore(now, false) { |
|||
vErr.Inner = errors.New("Token is not valid yet") |
|||
vErr.Errors |= ValidationErrorNotValidYet |
|||
} |
|||
|
|||
if vErr.valid() { |
|||
return nil |
|||
} |
|||
|
|||
return vErr |
|||
} |
@ -0,0 +1,52 @@ |
|||
package jwt |
|||
|
|||
// Implements the none signing method. This is required by the spec
|
|||
// but you probably should never use it.
|
|||
var SigningMethodNone *signingMethodNone |
|||
|
|||
const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed" |
|||
|
|||
var NoneSignatureTypeDisallowedError error |
|||
|
|||
type signingMethodNone struct{} |
|||
type unsafeNoneMagicConstant string |
|||
|
|||
func init() { |
|||
SigningMethodNone = &signingMethodNone{} |
|||
NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid) |
|||
|
|||
RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod { |
|||
return SigningMethodNone |
|||
}) |
|||
} |
|||
|
|||
func (m *signingMethodNone) Alg() string { |
|||
return "none" |
|||
} |
|||
|
|||
// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key
|
|||
func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) { |
|||
// Key must be UnsafeAllowNoneSignatureType to prevent accidentally
|
|||
// accepting 'none' signing method
|
|||
if _, ok := key.(unsafeNoneMagicConstant); !ok { |
|||
return NoneSignatureTypeDisallowedError |
|||
} |
|||
// If signing method is none, signature must be an empty string
|
|||
if signature != "" { |
|||
return NewValidationError( |
|||
"'none' signing method with non-empty signature", |
|||
ValidationErrorSignatureInvalid, |
|||
) |
|||
} |
|||
|
|||
// Accept 'none' signing method.
|
|||
return nil |
|||
} |
|||
|
|||
// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key
|
|||
func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) { |
|||
if _, ok := key.(unsafeNoneMagicConstant); ok { |
|||
return "", nil |
|||
} |
|||
return "", NoneSignatureTypeDisallowedError |
|||
} |
@ -0,0 +1,148 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"bytes" |
|||
"encoding/json" |
|||
"fmt" |
|||
"strings" |
|||
) |
|||
|
|||
type Parser struct { |
|||
ValidMethods []string // If populated, only these methods will be considered valid
|
|||
UseJSONNumber bool // Use JSON Number format in JSON decoder
|
|||
SkipClaimsValidation bool // Skip claims validation during token parsing
|
|||
} |
|||
|
|||
// Parse, validate, and return a token.
|
|||
// keyFunc will receive the parsed token and should return the key for validating.
|
|||
// If everything is kosher, err will be nil
|
|||
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { |
|||
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) |
|||
} |
|||
|
|||
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { |
|||
token, parts, err := p.ParseUnverified(tokenString, claims) |
|||
if err != nil { |
|||
return token, err |
|||
} |
|||
|
|||
// Verify signing method is in the required set
|
|||
if p.ValidMethods != nil { |
|||
var signingMethodValid = false |
|||
var alg = token.Method.Alg() |
|||
for _, m := range p.ValidMethods { |
|||
if m == alg { |
|||
signingMethodValid = true |
|||
break |
|||
} |
|||
} |
|||
if !signingMethodValid { |
|||
// signing method is not in the listed set
|
|||
return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid) |
|||
} |
|||
} |
|||
|
|||
// Lookup key
|
|||
var key interface{} |
|||
if keyFunc == nil { |
|||
// keyFunc was not provided. short circuiting validation
|
|||
return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable) |
|||
} |
|||
if key, err = keyFunc(token); err != nil { |
|||
// keyFunc returned an error
|
|||
if ve, ok := err.(*ValidationError); ok { |
|||
return token, ve |
|||
} |
|||
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} |
|||
} |
|||
|
|||
vErr := &ValidationError{} |
|||
|
|||
// Validate Claims
|
|||
if !p.SkipClaimsValidation { |
|||
if err := token.Claims.Valid(); err != nil { |
|||
|
|||
// If the Claims Valid returned an error, check if it is a validation error,
|
|||
// If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set
|
|||
if e, ok := err.(*ValidationError); !ok { |
|||
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid} |
|||
} else { |
|||
vErr = e |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Perform validation
|
|||
token.Signature = parts[2] |
|||
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { |
|||
vErr.Inner = err |
|||
vErr.Errors |= ValidationErrorSignatureInvalid |
|||
} |
|||
|
|||
if vErr.valid() { |
|||
token.Valid = true |
|||
return token, nil |
|||
} |
|||
|
|||
return token, vErr |
|||
} |
|||
|
|||
// WARNING: Don't use this method unless you know what you're doing
|
|||
//
|
|||
// This method parses the token but doesn't validate the signature. It's only
|
|||
// ever useful in cases where you know the signature is valid (because it has
|
|||
// been checked previously in the stack) and you want to extract values from
|
|||
// it.
|
|||
func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { |
|||
parts = strings.Split(tokenString, ".") |
|||
if len(parts) != 3 { |
|||
return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) |
|||
} |
|||
|
|||
token = &Token{Raw: tokenString} |
|||
|
|||
// parse Header
|
|||
var headerBytes []byte |
|||
if headerBytes, err = DecodeSegment(parts[0]); err != nil { |
|||
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { |
|||
return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed) |
|||
} |
|||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} |
|||
} |
|||
if err = json.Unmarshal(headerBytes, &token.Header); err != nil { |
|||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} |
|||
} |
|||
|
|||
// parse Claims
|
|||
var claimBytes []byte |
|||
token.Claims = claims |
|||
|
|||
if claimBytes, err = DecodeSegment(parts[1]); err != nil { |
|||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} |
|||
} |
|||
dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) |
|||
if p.UseJSONNumber { |
|||
dec.UseNumber() |
|||
} |
|||
// JSON Decode. Special case for map type to avoid weird pointer behavior
|
|||
if c, ok := token.Claims.(MapClaims); ok { |
|||
err = dec.Decode(&c) |
|||
} else { |
|||
err = dec.Decode(&claims) |
|||
} |
|||
// Handle decode error
|
|||
if err != nil { |
|||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} |
|||
} |
|||
|
|||
// Lookup signature method
|
|||
if method, ok := token.Header["alg"].(string); ok { |
|||
if token.Method = GetSigningMethod(method); token.Method == nil { |
|||
return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable) |
|||
} |
|||
} else { |
|||
return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable) |
|||
} |
|||
|
|||
return token, parts, nil |
|||
} |
@ -0,0 +1,101 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto" |
|||
"crypto/rand" |
|||
"crypto/rsa" |
|||
) |
|||
|
|||
// Implements the RSA family of signing methods signing methods
|
|||
// Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation
|
|||
type SigningMethodRSA struct { |
|||
Name string |
|||
Hash crypto.Hash |
|||
} |
|||
|
|||
// Specific instances for RS256 and company
|
|||
var ( |
|||
SigningMethodRS256 *SigningMethodRSA |
|||
SigningMethodRS384 *SigningMethodRSA |
|||
SigningMethodRS512 *SigningMethodRSA |
|||
) |
|||
|
|||
func init() { |
|||
// RS256
|
|||
SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256} |
|||
RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod { |
|||
return SigningMethodRS256 |
|||
}) |
|||
|
|||
// RS384
|
|||
SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384} |
|||
RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod { |
|||
return SigningMethodRS384 |
|||
}) |
|||
|
|||
// RS512
|
|||
SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512} |
|||
RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod { |
|||
return SigningMethodRS512 |
|||
}) |
|||
} |
|||
|
|||
func (m *SigningMethodRSA) Alg() string { |
|||
return m.Name |
|||
} |
|||
|
|||
// Implements the Verify method from SigningMethod
|
|||
// For this signing method, must be an *rsa.PublicKey structure.
|
|||
func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { |
|||
var err error |
|||
|
|||
// Decode the signature
|
|||
var sig []byte |
|||
if sig, err = DecodeSegment(signature); err != nil { |
|||
return err |
|||
} |
|||
|
|||
var rsaKey *rsa.PublicKey |
|||
var ok bool |
|||
|
|||
if rsaKey, ok = key.(*rsa.PublicKey); !ok { |
|||
return ErrInvalidKeyType |
|||
} |
|||
|
|||
// Create hasher
|
|||
if !m.Hash.Available() { |
|||
return ErrHashUnavailable |
|||
} |
|||
hasher := m.Hash.New() |
|||
hasher.Write([]byte(signingString)) |
|||
|
|||
// Verify the signature
|
|||
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) |
|||
} |
|||
|
|||
// Implements the Sign method from SigningMethod
|
|||
// For this signing method, must be an *rsa.PrivateKey structure.
|
|||
func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { |
|||
var rsaKey *rsa.PrivateKey |
|||
var ok bool |
|||
|
|||
// Validate type of key
|
|||
if rsaKey, ok = key.(*rsa.PrivateKey); !ok { |
|||
return "", ErrInvalidKey |
|||
} |
|||
|
|||
// Create the hasher
|
|||
if !m.Hash.Available() { |
|||
return "", ErrHashUnavailable |
|||
} |
|||
|
|||
hasher := m.Hash.New() |
|||
hasher.Write([]byte(signingString)) |
|||
|
|||
// Sign the string and return the encoded bytes
|
|||
if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil { |
|||
return EncodeSegment(sigBytes), nil |
|||
} else { |
|||
return "", err |
|||
} |
|||
} |
@ -0,0 +1,142 @@ |
|||
// +build go1.4
|
|||
|
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto" |
|||
"crypto/rand" |
|||
"crypto/rsa" |
|||
) |
|||
|
|||
// Implements the RSAPSS family of signing methods signing methods
|
|||
type SigningMethodRSAPSS struct { |
|||
*SigningMethodRSA |
|||
Options *rsa.PSSOptions |
|||
// VerifyOptions is optional. If set overrides Options for rsa.VerifyPPS.
|
|||
// Used to accept tokens signed with rsa.PSSSaltLengthAuto, what doesn't follow
|
|||
// https://tools.ietf.org/html/rfc7518#section-3.5 but was used previously.
|
|||
// See https://github.com/dgrijalva/jwt-go/issues/285#issuecomment-437451244 for details.
|
|||
VerifyOptions *rsa.PSSOptions |
|||
} |
|||
|
|||
// Specific instances for RS/PS and company.
|
|||
var ( |
|||
SigningMethodPS256 *SigningMethodRSAPSS |
|||
SigningMethodPS384 *SigningMethodRSAPSS |
|||
SigningMethodPS512 *SigningMethodRSAPSS |
|||
) |
|||
|
|||
func init() { |
|||
// PS256
|
|||
SigningMethodPS256 = &SigningMethodRSAPSS{ |
|||
SigningMethodRSA: &SigningMethodRSA{ |
|||
Name: "PS256", |
|||
Hash: crypto.SHA256, |
|||
}, |
|||
Options: &rsa.PSSOptions{ |
|||
SaltLength: rsa.PSSSaltLengthEqualsHash, |
|||
}, |
|||
VerifyOptions: &rsa.PSSOptions{ |
|||
SaltLength: rsa.PSSSaltLengthAuto, |
|||
}, |
|||
} |
|||
RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { |
|||
return SigningMethodPS256 |
|||
}) |
|||
|
|||
// PS384
|
|||
SigningMethodPS384 = &SigningMethodRSAPSS{ |
|||
SigningMethodRSA: &SigningMethodRSA{ |
|||
Name: "PS384", |
|||
Hash: crypto.SHA384, |
|||
}, |
|||
Options: &rsa.PSSOptions{ |
|||
SaltLength: rsa.PSSSaltLengthEqualsHash, |
|||
}, |
|||
VerifyOptions: &rsa.PSSOptions{ |
|||
SaltLength: rsa.PSSSaltLengthAuto, |
|||
}, |
|||
} |
|||
RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { |
|||
return SigningMethodPS384 |
|||
}) |
|||
|
|||
// PS512
|
|||
SigningMethodPS512 = &SigningMethodRSAPSS{ |
|||
SigningMethodRSA: &SigningMethodRSA{ |
|||
Name: "PS512", |
|||
Hash: crypto.SHA512, |
|||
}, |
|||
Options: &rsa.PSSOptions{ |
|||
SaltLength: rsa.PSSSaltLengthEqualsHash, |
|||
}, |
|||
VerifyOptions: &rsa.PSSOptions{ |
|||
SaltLength: rsa.PSSSaltLengthAuto, |
|||
}, |
|||
} |
|||
RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { |
|||
return SigningMethodPS512 |
|||
}) |
|||
} |
|||
|
|||
// Implements the Verify method from SigningMethod
|
|||
// For this verify method, key must be an rsa.PublicKey struct
|
|||
func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { |
|||
var err error |
|||
|
|||
// Decode the signature
|
|||
var sig []byte |
|||
if sig, err = DecodeSegment(signature); err != nil { |
|||
return err |
|||
} |
|||
|
|||
var rsaKey *rsa.PublicKey |
|||
switch k := key.(type) { |
|||
case *rsa.PublicKey: |
|||
rsaKey = k |
|||
default: |
|||
return ErrInvalidKey |
|||
} |
|||
|
|||
// Create hasher
|
|||
if !m.Hash.Available() { |
|||
return ErrHashUnavailable |
|||
} |
|||
hasher := m.Hash.New() |
|||
hasher.Write([]byte(signingString)) |
|||
|
|||
opts := m.Options |
|||
if m.VerifyOptions != nil { |
|||
opts = m.VerifyOptions |
|||
} |
|||
|
|||
return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts) |
|||
} |
|||
|
|||
// Implements the Sign method from SigningMethod
|
|||
// For this signing method, key must be an rsa.PrivateKey struct
|
|||
func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { |
|||
var rsaKey *rsa.PrivateKey |
|||
|
|||
switch k := key.(type) { |
|||
case *rsa.PrivateKey: |
|||
rsaKey = k |
|||
default: |
|||
return "", ErrInvalidKeyType |
|||
} |
|||
|
|||
// Create the hasher
|
|||
if !m.Hash.Available() { |
|||
return "", ErrHashUnavailable |
|||
} |
|||
|
|||
hasher := m.Hash.New() |
|||
hasher.Write([]byte(signingString)) |
|||
|
|||
// Sign the string and return the encoded bytes
|
|||
if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { |
|||
return EncodeSegment(sigBytes), nil |
|||
} else { |
|||
return "", err |
|||
} |
|||
} |
@ -0,0 +1,101 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"crypto/rsa" |
|||
"crypto/x509" |
|||
"encoding/pem" |
|||
"errors" |
|||
) |
|||
|
|||
var ( |
|||
ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be a PEM encoded PKCS1 or PKCS8 key") |
|||
ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key") |
|||
ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key") |
|||
) |
|||
|
|||
// Parse PEM encoded PKCS1 or PKCS8 private key
|
|||
func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { |
|||
var err error |
|||
|
|||
// Parse PEM block
|
|||
var block *pem.Block |
|||
if block, _ = pem.Decode(key); block == nil { |
|||
return nil, ErrKeyMustBePEMEncoded |
|||
} |
|||
|
|||
var parsedKey interface{} |
|||
if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { |
|||
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
var pkey *rsa.PrivateKey |
|||
var ok bool |
|||
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { |
|||
return nil, ErrNotRSAPrivateKey |
|||
} |
|||
|
|||
return pkey, nil |
|||
} |
|||
|
|||
// Parse PEM encoded PKCS1 or PKCS8 private key protected with password
|
|||
func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) { |
|||
var err error |
|||
|
|||
// Parse PEM block
|
|||
var block *pem.Block |
|||
if block, _ = pem.Decode(key); block == nil { |
|||
return nil, ErrKeyMustBePEMEncoded |
|||
} |
|||
|
|||
var parsedKey interface{} |
|||
|
|||
var blockDecrypted []byte |
|||
if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
if parsedKey, err = x509.ParsePKCS1PrivateKey(blockDecrypted); err != nil { |
|||
if parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
var pkey *rsa.PrivateKey |
|||
var ok bool |
|||
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { |
|||
return nil, ErrNotRSAPrivateKey |
|||
} |
|||
|
|||
return pkey, nil |
|||
} |
|||
|
|||
// Parse PEM encoded PKCS1 or PKCS8 public key
|
|||
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { |
|||
var err error |
|||
|
|||
// Parse PEM block
|
|||
var block *pem.Block |
|||
if block, _ = pem.Decode(key); block == nil { |
|||
return nil, ErrKeyMustBePEMEncoded |
|||
} |
|||
|
|||
// Parse the key
|
|||
var parsedKey interface{} |
|||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { |
|||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil { |
|||
parsedKey = cert.PublicKey |
|||
} else { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
var pkey *rsa.PublicKey |
|||
var ok bool |
|||
if pkey, ok = parsedKey.(*rsa.PublicKey); !ok { |
|||
return nil, ErrNotRSAPublicKey |
|||
} |
|||
|
|||
return pkey, nil |
|||
} |
@ -0,0 +1,35 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"sync" |
|||
) |
|||
|
|||
var signingMethods = map[string]func() SigningMethod{} |
|||
var signingMethodLock = new(sync.RWMutex) |
|||
|
|||
// Implement SigningMethod to add new methods for signing or verifying tokens.
|
|||
type SigningMethod interface { |
|||
Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid
|
|||
Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error
|
|||
Alg() string // returns the alg identifier for this method (example: 'HS256')
|
|||
} |
|||
|
|||
// Register the "alg" name and a factory function for signing method.
|
|||
// This is typically done during init() in the method's implementation
|
|||
func RegisterSigningMethod(alg string, f func() SigningMethod) { |
|||
signingMethodLock.Lock() |
|||
defer signingMethodLock.Unlock() |
|||
|
|||
signingMethods[alg] = f |
|||
} |
|||
|
|||
// Get a signing method from an "alg" string
|
|||
func GetSigningMethod(alg string) (method SigningMethod) { |
|||
signingMethodLock.RLock() |
|||
defer signingMethodLock.RUnlock() |
|||
|
|||
if methodF, ok := signingMethods[alg]; ok { |
|||
method = methodF() |
|||
} |
|||
return |
|||
} |
@ -0,0 +1,104 @@ |
|||
package jwt |
|||
|
|||
import ( |
|||
"encoding/base64" |
|||
"encoding/json" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time).
|
|||
// You can override it to use another time value. This is useful for testing or if your
|
|||
// server uses a different time zone than your tokens.
|
|||
var TimeFunc = time.Now |
|||
|
|||
// Parse methods use this callback function to supply
|
|||
// the key for verification. The function receives the parsed,
|
|||
// but unverified Token. This allows you to use properties in the
|
|||
// Header of the token (such as `kid`) to identify which key to use.
|
|||
type Keyfunc func(*Token) (interface{}, error) |
|||
|
|||
// A JWT Token. Different fields will be used depending on whether you're
|
|||
// creating or parsing/verifying a token.
|
|||
type Token struct { |
|||
Raw string // The raw token. Populated when you Parse a token
|
|||
Method SigningMethod // The signing method used or to be used
|
|||
Header map[string]interface{} // The first segment of the token
|
|||
Claims Claims // The second segment of the token
|
|||
Signature string // The third segment of the token. Populated when you Parse a token
|
|||
Valid bool // Is the token valid? Populated when you Parse/Verify a token
|
|||
} |
|||
|
|||
// Create a new Token. Takes a signing method
|
|||
func New(method SigningMethod) *Token { |
|||
return NewWithClaims(method, MapClaims{}) |
|||
} |
|||
|
|||
func NewWithClaims(method SigningMethod, claims Claims) *Token { |
|||
return &Token{ |
|||
Header: map[string]interface{}{ |
|||
"typ": "JWT", |
|||
"alg": method.Alg(), |
|||
}, |
|||
Claims: claims, |
|||
Method: method, |
|||
} |
|||
} |
|||
|
|||
// Get the complete, signed token
|
|||
func (t *Token) SignedString(key interface{}) (string, error) { |
|||
var sig, sstr string |
|||
var err error |
|||
if sstr, err = t.SigningString(); err != nil { |
|||
return "", err |
|||
} |
|||
if sig, err = t.Method.Sign(sstr, key); err != nil { |
|||
return "", err |
|||
} |
|||
return strings.Join([]string{sstr, sig}, "."), nil |
|||
} |
|||
|
|||
// Generate the signing string. This is the
|
|||
// most expensive part of the whole deal. Unless you
|
|||
// need this for something special, just go straight for
|
|||
// the SignedString.
|
|||
func (t *Token) SigningString() (string, error) { |
|||
var err error |
|||
parts := make([]string, 2) |
|||
for i := range parts { |
|||
var jsonValue []byte |
|||
if i == 0 { |
|||
if jsonValue, err = json.Marshal(t.Header); err != nil { |
|||
return "", err |
|||
} |
|||
} else { |
|||
if jsonValue, err = json.Marshal(t.Claims); err != nil { |
|||
return "", err |
|||
} |
|||
} |
|||
|
|||
parts[i] = EncodeSegment(jsonValue) |
|||
} |
|||
return strings.Join(parts, "."), nil |
|||
} |
|||
|
|||
// Parse, validate, and return a token.
|
|||
// keyFunc will receive the parsed token and should return the key for validating.
|
|||
// If everything is kosher, err will be nil
|
|||
func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { |
|||
return new(Parser).Parse(tokenString, keyFunc) |
|||
} |
|||
|
|||
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { |
|||
return new(Parser).ParseWithClaims(tokenString, claims, keyFunc) |
|||
} |
|||
|
|||
// Encode JWT specific base64url encoding with padding stripped
|
|||
func EncodeSegment(seg []byte) string { |
|||
return base64.RawURLEncoding.EncodeToString(seg) |
|||
} |
|||
|
|||
// Decode JWT specific base64url encoding with padding stripped
|
|||
func DecodeSegment(seg string) ([]byte, error) { |
|||
return base64.RawURLEncoding.DecodeString(seg) |
|||
} |
@ -0,0 +1,22 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2018 labstack |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
|
@ -0,0 +1,86 @@ |
|||
# Color |
|||
|
|||
Style terminal text. |
|||
|
|||
## Installation |
|||
|
|||
```sh |
|||
go get github.com/labstack/gommon/color |
|||
``` |
|||
|
|||
## Windows? |
|||
|
|||
Try [cmder](http://bliker.github.io/cmder) or https://github.com/mattn/go-colorable |
|||
|
|||
## [Usage](https://github.com/labstack/gommon/blob/master/color/color_test.go) |
|||
|
|||
```sh |
|||
import github.com/labstack/gommon/color |
|||
``` |
|||
|
|||
### Colored text |
|||
|
|||
```go |
|||
color.Println(color.Black("black")) |
|||
color.Println(color.Red("red")) |
|||
color.Println(color.Green("green")) |
|||
color.Println(color.Yellow("yellow")) |
|||
color.Println(color.Blue("blue")) |
|||
color.Println(color.Magenta("magenta")) |
|||
color.Println(color.Cyan("cyan")) |
|||
color.Println(color.White("white")) |
|||
color.Println(color.Grey("grey")) |
|||
``` |
|||
![Colored Text](http://i.imgur.com/8RtY1QR.png) |
|||
|
|||
### Colored background |
|||
|
|||
```go |
|||
color.Println(color.BlackBg("black background", color.Wht)) |
|||
color.Println(color.RedBg("red background")) |
|||
color.Println(color.GreenBg("green background")) |
|||
color.Println(color.YellowBg("yellow background")) |
|||
color.Println(color.BlueBg("blue background")) |
|||
color.Println(color.MagentaBg("magenta background")) |
|||
color.Println(color.CyanBg("cyan background")) |
|||
color.Println(color.WhiteBg("white background")) |
|||
``` |
|||
![Colored Background](http://i.imgur.com/SrrS6lw.png) |
|||
|
|||
### Emphasis |
|||
|
|||
```go |
|||
color.Println(color.Bold("bold")) |
|||
color.Println(color.Dim("dim")) |
|||
color.Println(color.Italic("italic")) |
|||
color.Println(color.Underline("underline")) |
|||
color.Println(color.Inverse("inverse")) |
|||
color.Println(color.Hidden("hidden")) |
|||
color.Println(color.Strikeout("strikeout")) |
|||
``` |
|||
![Emphasis](http://i.imgur.com/3RSJBbc.png) |
|||
|
|||
### Mix and match |
|||
|
|||
```go |
|||
color.Println(color.Green("bold green with white background", color.B, color.WhtBg)) |
|||
color.Println(color.Red("underline red", color.U)) |
|||
color.Println(color.Yellow("dim yellow", color.D)) |
|||
color.Println(color.Cyan("inverse cyan", color.In)) |
|||
color.Println(color.Blue("bold underline dim blue", color.B, color.U, color.D)) |
|||
``` |
|||
![Mix and match](http://i.imgur.com/jWGq9Ca.png) |
|||
|
|||
### Enable/Disable the package |
|||
|
|||
```go |
|||
color.Disable() |
|||
color.Enable() |
|||
``` |
|||
|
|||
### New instance |
|||
|
|||
```go |
|||
c := New() |
|||
c.Green("green") |
|||
``` |
@ -0,0 +1,407 @@ |
|||
package color |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"io" |
|||
"os" |
|||
|
|||
"github.com/mattn/go-colorable" |
|||
"github.com/mattn/go-isatty" |
|||
) |
|||
|
|||
type ( |
|||
inner func(interface{}, []string, *Color) string |
|||
) |
|||
|
|||
// Color styles
|
|||
const ( |
|||
// Blk Black text style
|
|||
Blk = "30" |
|||
// Rd red text style
|
|||
Rd = "31" |
|||
// Grn green text style
|
|||
Grn = "32" |
|||
// Yel yellow text style
|
|||
Yel = "33" |
|||
// Blu blue text style
|
|||
Blu = "34" |
|||
// Mgn magenta text style
|
|||
Mgn = "35" |
|||
// Cyn cyan text style
|
|||
Cyn = "36" |
|||
// Wht white text style
|
|||
Wht = "37" |
|||
// Gry grey text style
|
|||
Gry = "90" |
|||
|
|||
// BlkBg black background style
|
|||
BlkBg = "40" |
|||
// RdBg red background style
|
|||
RdBg = "41" |
|||
// GrnBg green background style
|
|||
GrnBg = "42" |
|||
// YelBg yellow background style
|
|||
YelBg = "43" |
|||
// BluBg blue background style
|
|||
BluBg = "44" |
|||
// MgnBg magenta background style
|
|||
MgnBg = "45" |
|||
// CynBg cyan background style
|
|||
CynBg = "46" |
|||
// WhtBg white background style
|
|||
WhtBg = "47" |
|||
|
|||
// R reset emphasis style
|
|||
R = "0" |
|||
// B bold emphasis style
|
|||
B = "1" |
|||
// D dim emphasis style
|
|||
D = "2" |
|||
// I italic emphasis style
|
|||
I = "3" |
|||
// U underline emphasis style
|
|||
U = "4" |
|||
// In inverse emphasis style
|
|||
In = "7" |
|||
// H hidden emphasis style
|
|||
H = "8" |
|||
// S strikeout emphasis style
|
|||
S = "9" |
|||
) |
|||
|
|||
var ( |
|||
black = outer(Blk) |
|||
red = outer(Rd) |
|||
green = outer(Grn) |
|||
yellow = outer(Yel) |
|||
blue = outer(Blu) |
|||
magenta = outer(Mgn) |
|||
cyan = outer(Cyn) |
|||
white = outer(Wht) |
|||
grey = outer(Gry) |
|||
|
|||
blackBg = outer(BlkBg) |
|||
redBg = outer(RdBg) |
|||
greenBg = outer(GrnBg) |
|||
yellowBg = outer(YelBg) |
|||
blueBg = outer(BluBg) |
|||
magentaBg = outer(MgnBg) |
|||
cyanBg = outer(CynBg) |
|||
whiteBg = outer(WhtBg) |
|||
|
|||
reset = outer(R) |
|||
bold = outer(B) |
|||
dim = outer(D) |
|||
italic = outer(I) |
|||
underline = outer(U) |
|||
inverse = outer(In) |
|||
hidden = outer(H) |
|||
strikeout = outer(S) |
|||
|
|||
global = New() |
|||
) |
|||
|
|||
func outer(n string) inner { |
|||
return func(msg interface{}, styles []string, c *Color) string { |
|||
// TODO: Drop fmt to boost performance?
|
|||
if c.disabled { |
|||
return fmt.Sprintf("%v", msg) |
|||
} |
|||
|
|||
b := new(bytes.Buffer) |
|||
b.WriteString("\x1b[") |
|||
b.WriteString(n) |
|||
for _, s := range styles { |
|||
b.WriteString(";") |
|||
b.WriteString(s) |
|||
} |
|||
b.WriteString("m") |
|||
return fmt.Sprintf("%s%v\x1b[0m", b.String(), msg) |
|||
} |
|||
} |
|||
|
|||
type ( |
|||
Color struct { |
|||
output io.Writer |
|||
disabled bool |
|||
} |
|||
) |
|||
|
|||
// New creates a Color instance.
|
|||
func New() (c *Color) { |
|||
c = new(Color) |
|||
c.SetOutput(colorable.NewColorableStdout()) |
|||
return |
|||
} |
|||
|
|||
// Output returns the output.
|
|||
func (c *Color) Output() io.Writer { |
|||
return c.output |
|||
} |
|||
|
|||
// SetOutput sets the output.
|
|||
func (c *Color) SetOutput(w io.Writer) { |
|||
c.output = w |
|||
if w, ok := w.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) { |
|||
c.disabled = true |
|||
} |
|||
} |
|||
|
|||
// Disable disables the colors and styles.
|
|||
func (c *Color) Disable() { |
|||
c.disabled = true |
|||
} |
|||
|
|||
// Enable enables the colors and styles.
|
|||
func (c *Color) Enable() { |
|||
c.disabled = false |
|||
} |
|||
|
|||
// Print is analogous to `fmt.Print` with termial detection.
|
|||
func (c *Color) Print(args ...interface{}) { |
|||
fmt.Fprint(c.output, args...) |
|||
} |
|||
|
|||
// Println is analogous to `fmt.Println` with termial detection.
|
|||
func (c *Color) Println(args ...interface{}) { |
|||
fmt.Fprintln(c.output, args...) |
|||
} |
|||
|
|||
// Printf is analogous to `fmt.Printf` with termial detection.
|
|||
func (c *Color) Printf(format string, args ...interface{}) { |
|||
fmt.Fprintf(c.output, format, args...) |
|||
} |
|||
|
|||
func (c *Color) Black(msg interface{}, styles ...string) string { |
|||
return black(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Red(msg interface{}, styles ...string) string { |
|||
return red(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Green(msg interface{}, styles ...string) string { |
|||
return green(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Yellow(msg interface{}, styles ...string) string { |
|||
return yellow(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Blue(msg interface{}, styles ...string) string { |
|||
return blue(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Magenta(msg interface{}, styles ...string) string { |
|||
return magenta(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Cyan(msg interface{}, styles ...string) string { |
|||
return cyan(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) White(msg interface{}, styles ...string) string { |
|||
return white(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Grey(msg interface{}, styles ...string) string { |
|||
return grey(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) BlackBg(msg interface{}, styles ...string) string { |
|||
return blackBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) RedBg(msg interface{}, styles ...string) string { |
|||
return redBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) GreenBg(msg interface{}, styles ...string) string { |
|||
return greenBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) YellowBg(msg interface{}, styles ...string) string { |
|||
return yellowBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) BlueBg(msg interface{}, styles ...string) string { |
|||
return blueBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) MagentaBg(msg interface{}, styles ...string) string { |
|||
return magentaBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) CyanBg(msg interface{}, styles ...string) string { |
|||
return cyanBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) WhiteBg(msg interface{}, styles ...string) string { |
|||
return whiteBg(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Reset(msg interface{}, styles ...string) string { |
|||
return reset(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Bold(msg interface{}, styles ...string) string { |
|||
return bold(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Dim(msg interface{}, styles ...string) string { |
|||
return dim(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Italic(msg interface{}, styles ...string) string { |
|||
return italic(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Underline(msg interface{}, styles ...string) string { |
|||
return underline(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Inverse(msg interface{}, styles ...string) string { |
|||
return inverse(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Hidden(msg interface{}, styles ...string) string { |
|||
return hidden(msg, styles, c) |
|||
} |
|||
|
|||
func (c *Color) Strikeout(msg interface{}, styles ...string) string { |
|||
return strikeout(msg, styles, c) |
|||
} |
|||
|
|||
// Output returns the output.
|
|||
func Output() io.Writer { |
|||
return global.output |
|||
} |
|||
|
|||
// SetOutput sets the output.
|
|||
func SetOutput(w io.Writer) { |
|||
global.SetOutput(w) |
|||
} |
|||
|
|||
func Disable() { |
|||
global.Disable() |
|||
} |
|||
|
|||
func Enable() { |
|||
global.Enable() |
|||
} |
|||
|
|||
// Print is analogous to `fmt.Print` with termial detection.
|
|||
func Print(args ...interface{}) { |
|||
global.Print(args...) |
|||
} |
|||
|
|||
// Println is analogous to `fmt.Println` with termial detection.
|
|||
func Println(args ...interface{}) { |
|||
global.Println(args...) |
|||
} |
|||
|
|||
// Printf is analogous to `fmt.Printf` with termial detection.
|
|||
func Printf(format string, args ...interface{}) { |
|||
global.Printf(format, args...) |
|||
} |
|||
|
|||
func Black(msg interface{}, styles ...string) string { |
|||
return global.Black(msg, styles...) |
|||
} |
|||
|
|||
func Red(msg interface{}, styles ...string) string { |
|||
return global.Red(msg, styles...) |
|||
} |
|||
|
|||
func Green(msg interface{}, styles ...string) string { |
|||
return global.Green(msg, styles...) |
|||
} |
|||
|
|||
func Yellow(msg interface{}, styles ...string) string { |
|||
return global.Yellow(msg, styles...) |
|||
} |
|||
|
|||
func Blue(msg interface{}, styles ...string) string { |
|||
return global.Blue(msg, styles...) |
|||
} |
|||
|
|||
func Magenta(msg interface{}, styles ...string) string { |
|||
return global.Magenta(msg, styles...) |
|||
} |
|||
|
|||
func Cyan(msg interface{}, styles ...string) string { |
|||
return global.Cyan(msg, styles...) |
|||
} |
|||
|
|||
func White(msg interface{}, styles ...string) string { |
|||
return global.White(msg, styles...) |
|||
} |
|||
|
|||
func Grey(msg interface{}, styles ...string) string { |
|||
return global.Grey(msg, styles...) |
|||
} |
|||
|
|||
func BlackBg(msg interface{}, styles ...string) string { |
|||
return global.BlackBg(msg, styles...) |
|||
} |
|||
|
|||
func RedBg(msg interface{}, styles ...string) string { |
|||
return global.RedBg(msg, styles...) |
|||
} |
|||
|
|||
func GreenBg(msg interface{}, styles ...string) string { |
|||
return global.GreenBg(msg, styles...) |
|||
} |
|||
|
|||
func YellowBg(msg interface{}, styles ...string) string { |
|||
return global.YellowBg(msg, styles...) |
|||
} |
|||
|
|||
func BlueBg(msg interface{}, styles ...string) string { |
|||
return global.BlueBg(msg, styles...) |
|||
} |
|||
|
|||
func MagentaBg(msg interface{}, styles ...string) string { |
|||
return global.MagentaBg(msg, styles...) |
|||
} |
|||
|
|||
func CyanBg(msg interface{}, styles ...string) string { |
|||
return global.CyanBg(msg, styles...) |
|||
} |
|||
|
|||
func WhiteBg(msg interface{}, styles ...string) string { |
|||
return global.WhiteBg(msg, styles...) |
|||
} |
|||
|
|||
func Reset(msg interface{}, styles ...string) string { |
|||
return global.Reset(msg, styles...) |
|||
} |
|||
|
|||
func Bold(msg interface{}, styles ...string) string { |
|||
return global.Bold(msg, styles...) |
|||
} |
|||
|
|||
func Dim(msg interface{}, styles ...string) string { |
|||
return global.Dim(msg, styles...) |
|||
} |
|||
|
|||
func Italic(msg interface{}, styles ...string) string { |
|||
return global.Italic(msg, styles...) |
|||
} |
|||
|
|||
func Underline(msg interface{}, styles ...string) string { |
|||
return global.Underline(msg, styles...) |
|||
} |
|||
|
|||
func Inverse(msg interface{}, styles ...string) string { |
|||
return global.Inverse(msg, styles...) |
|||
} |
|||
|
|||
func Hidden(msg interface{}, styles ...string) string { |
|||
return global.Hidden(msg, styles...) |
|||
} |
|||
|
|||
func Strikeout(msg interface{}, styles ...string) string { |
|||
return global.Strikeout(msg, styles...) |
|||
} |
@ -0,0 +1,21 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2016 Yasuhiro Matsumoto |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
@ -0,0 +1,48 @@ |
|||
# go-colorable |
|||
|
|||
[![Build Status](https://github.com/mattn/go-colorable/workflows/test/badge.svg)](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest) |
|||
[![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable) |
|||
[![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) |
|||
[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) |
|||
|
|||
Colorable writer for windows. |
|||
|
|||
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) |
|||
This package is possible to handle escape sequence for ansi color on windows. |
|||
|
|||
## Too Bad! |
|||
|
|||
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) |
|||
|
|||
|
|||
## So Good! |
|||
|
|||
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) |
|||
|
|||
## Usage |
|||
|
|||
```go |
|||
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) |
|||
logrus.SetOutput(colorable.NewColorableStdout()) |
|||
|
|||
logrus.Info("succeeded") |
|||
logrus.Warn("not correct") |
|||
logrus.Error("something error") |
|||
logrus.Fatal("panic") |
|||
``` |
|||
|
|||
You can compile above code on non-windows OSs. |
|||
|
|||
## Installation |
|||
|
|||
``` |
|||
$ go get github.com/mattn/go-colorable |
|||
``` |
|||
|
|||
# License |
|||
|
|||
MIT |
|||
|
|||
# Author |
|||
|
|||
Yasuhiro Matsumoto (a.k.a mattn) |
@ -0,0 +1,38 @@ |
|||
//go:build appengine
|
|||
// +build appengine
|
|||
|
|||
package colorable |
|||
|
|||
import ( |
|||
"io" |
|||
"os" |
|||
|
|||
_ "github.com/mattn/go-isatty" |
|||
) |
|||
|
|||
// NewColorable returns new instance of Writer which handles escape sequence.
|
|||
func NewColorable(file *os.File) io.Writer { |
|||
if file == nil { |
|||
panic("nil passed instead of *os.File to NewColorable()") |
|||
} |
|||
|
|||
return file |
|||
} |
|||
|
|||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
|||
func NewColorableStdout() io.Writer { |
|||
return os.Stdout |
|||
} |
|||
|
|||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
|||
func NewColorableStderr() io.Writer { |
|||
return os.Stderr |
|||
} |
|||
|
|||
// EnableColorsStdout enable colors if possible.
|
|||
func EnableColorsStdout(enabled *bool) func() { |
|||
if enabled != nil { |
|||
*enabled = true |
|||
} |
|||
return func() {} |
|||
} |
@ -0,0 +1,38 @@ |
|||
//go:build !windows && !appengine
|
|||
// +build !windows,!appengine
|
|||
|
|||
package colorable |
|||
|
|||
import ( |
|||
"io" |
|||
"os" |
|||
|
|||
_ "github.com/mattn/go-isatty" |
|||
) |
|||
|
|||
// NewColorable returns new instance of Writer which handles escape sequence.
|
|||
func NewColorable(file *os.File) io.Writer { |
|||
if file == nil { |
|||
panic("nil passed instead of *os.File to NewColorable()") |
|||
} |
|||
|
|||
return file |
|||
} |
|||
|
|||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
|||
func NewColorableStdout() io.Writer { |
|||
return os.Stdout |
|||
} |
|||
|
|||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
|||
func NewColorableStderr() io.Writer { |
|||
return os.Stderr |
|||
} |
|||
|
|||
// EnableColorsStdout enable colors if possible.
|
|||
func EnableColorsStdout(enabled *bool) func() { |
|||
if enabled != nil { |
|||
*enabled = true |
|||
} |
|||
return func() {} |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,12 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
set -e |
|||
echo "" > coverage.txt |
|||
|
|||
for d in $(go list ./... | grep -v vendor); do |
|||
go test -race -coverprofile=profile.out -covermode=atomic "$d" |
|||
if [ -f profile.out ]; then |
|||
cat profile.out >> coverage.txt |
|||
rm profile.out |
|||
fi |
|||
done |
@ -0,0 +1,59 @@ |
|||
package colorable |
|||
|
|||
import ( |
|||
"bytes" |
|||
"io" |
|||
) |
|||
|
|||
// NonColorable holds writer but removes escape sequence.
|
|||
type NonColorable struct { |
|||
out io.Writer |
|||
} |
|||
|
|||
// NewNonColorable returns new instance of Writer which removes escape sequence from Writer.
|
|||
func NewNonColorable(w io.Writer) io.Writer { |
|||
return &NonColorable{out: w} |
|||
} |
|||
|
|||
// Write writes data on console
|
|||
func (w *NonColorable) Write(data []byte) (n int, err error) { |
|||
er := bytes.NewReader(data) |
|||
var plaintext bytes.Buffer |
|||
loop: |
|||
for { |
|||
c1, err := er.ReadByte() |
|||
if err != nil { |
|||
plaintext.WriteTo(w.out) |
|||
break loop |
|||
} |
|||
if c1 != 0x1b { |
|||
plaintext.WriteByte(c1) |
|||
continue |
|||
} |
|||
_, err = plaintext.WriteTo(w.out) |
|||
if err != nil { |
|||
break loop |
|||
} |
|||
c2, err := er.ReadByte() |
|||
if err != nil { |
|||
break loop |
|||
} |
|||
if c2 != 0x5b { |
|||
continue |
|||
} |
|||
|
|||
var buf bytes.Buffer |
|||
for { |
|||
c, err := er.ReadByte() |
|||
if err != nil { |
|||
break loop |
|||
} |
|||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { |
|||
break |
|||
} |
|||
buf.Write([]byte(string(c))) |
|||
} |
|||
} |
|||
|
|||
return len(data), nil |
|||
} |
@ -0,0 +1,9 @@ |
|||
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com> |
|||
|
|||
MIT License (Expat) |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,50 @@ |
|||
# go-isatty |
|||
|
|||
[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) |
|||
[![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty) |
|||
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) |
|||
[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) |
|||
|
|||
isatty for golang |
|||
|
|||
## Usage |
|||
|
|||
```go |
|||
package main |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/mattn/go-isatty" |
|||
"os" |
|||
) |
|||
|
|||
func main() { |
|||
if isatty.IsTerminal(os.Stdout.Fd()) { |
|||
fmt.Println("Is Terminal") |
|||
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { |
|||
fmt.Println("Is Cygwin/MSYS2 Terminal") |
|||
} else { |
|||
fmt.Println("Is Not Terminal") |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Installation |
|||
|
|||
``` |
|||
$ go get github.com/mattn/go-isatty |
|||
``` |
|||
|
|||
## License |
|||
|
|||
MIT |
|||
|
|||
## Author |
|||
|
|||
Yasuhiro Matsumoto (a.k.a mattn) |
|||
|
|||
## Thanks |
|||
|
|||
* k-takata: base idea for IsCygwinTerminal |
|||
|
|||
https://github.com/k-takata/go-iscygpty |
@ -0,0 +1,2 @@ |
|||
// Package isatty implements interface to isatty
|
|||
package isatty |
@ -0,0 +1,12 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
set -e |
|||
echo "" > coverage.txt |
|||
|
|||
for d in $(go list ./... | grep -v vendor); do |
|||
go test -race -coverprofile=profile.out -covermode=atomic "$d" |
|||
if [ -f profile.out ]; then |
|||
cat profile.out >> coverage.txt |
|||
rm profile.out |
|||
fi |
|||
done |
@ -0,0 +1,19 @@ |
|||
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine
|
|||
// +build darwin freebsd openbsd netbsd dragonfly
|
|||
// +build !appengine
|
|||
|
|||
package isatty |
|||
|
|||
import "golang.org/x/sys/unix" |
|||
|
|||
// IsTerminal return true if the file descriptor is terminal.
|
|||
func IsTerminal(fd uintptr) bool { |
|||
_, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA) |
|||
return err == nil |
|||
} |
|||
|
|||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
|||
// terminal. This is also always false on this environment.
|
|||
func IsCygwinTerminal(fd uintptr) bool { |
|||
return false |
|||
} |
@ -0,0 +1,16 @@ |
|||
//go:build appengine || js || nacl || wasm
|
|||
// +build appengine js nacl wasm
|
|||
|
|||
package isatty |
|||
|
|||
// IsTerminal returns true if the file descriptor is terminal which
|
|||
// is always false on js and appengine classic which is a sandboxed PaaS.
|
|||
func IsTerminal(fd uintptr) bool { |
|||
return false |
|||
} |
|||
|
|||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
|||
// terminal. This is also always false on this environment.
|
|||
func IsCygwinTerminal(fd uintptr) bool { |
|||
return false |
|||
} |
@ -0,0 +1,23 @@ |
|||
//go:build plan9
|
|||
// +build plan9
|
|||
|
|||
package isatty |
|||
|
|||
import ( |
|||
"syscall" |
|||
) |
|||
|
|||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|||
func IsTerminal(fd uintptr) bool { |
|||
path, err := syscall.Fd2path(int(fd)) |
|||
if err != nil { |
|||
return false |
|||
} |
|||
return path == "/dev/cons" || path == "/mnt/term/dev/cons" |
|||
} |
|||
|
|||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
|||
// terminal. This is also always false on this environment.
|
|||
func IsCygwinTerminal(fd uintptr) bool { |
|||
return false |
|||
} |
@ -0,0 +1,21 @@ |
|||
//go:build solaris && !appengine
|
|||
// +build solaris,!appengine
|
|||
|
|||
package isatty |
|||
|
|||
import ( |
|||
"golang.org/x/sys/unix" |
|||
) |
|||
|
|||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|||
// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c
|
|||
func IsTerminal(fd uintptr) bool { |
|||
_, err := unix.IoctlGetTermio(int(fd), unix.TCGETA) |
|||
return err == nil |
|||
} |
|||
|
|||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
|||
// terminal. This is also always false on this environment.
|
|||
func IsCygwinTerminal(fd uintptr) bool { |
|||
return false |
|||
} |
@ -0,0 +1,19 @@ |
|||
//go:build (linux || aix || zos) && !appengine
|
|||
// +build linux aix zos
|
|||
// +build !appengine
|
|||
|
|||
package isatty |
|||
|
|||
import "golang.org/x/sys/unix" |
|||
|
|||
// IsTerminal return true if the file descriptor is terminal.
|
|||
func IsTerminal(fd uintptr) bool { |
|||
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) |
|||
return err == nil |
|||
} |
|||
|
|||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
|||
// terminal. This is also always false on this environment.
|
|||
func IsCygwinTerminal(fd uintptr) bool { |
|||
return false |
|||
} |
@ -0,0 +1,125 @@ |
|||
//go:build windows && !appengine
|
|||
// +build windows,!appengine
|
|||
|
|||
package isatty |
|||
|
|||
import ( |
|||
"errors" |
|||
"strings" |
|||
"syscall" |
|||
"unicode/utf16" |
|||
"unsafe" |
|||
) |
|||
|
|||
const ( |
|||
objectNameInfo uintptr = 1 |
|||
fileNameInfo = 2 |
|||
fileTypePipe = 3 |
|||
) |
|||
|
|||
var ( |
|||
kernel32 = syscall.NewLazyDLL("kernel32.dll") |
|||
ntdll = syscall.NewLazyDLL("ntdll.dll") |
|||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode") |
|||
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") |
|||
procGetFileType = kernel32.NewProc("GetFileType") |
|||
procNtQueryObject = ntdll.NewProc("NtQueryObject") |
|||
) |
|||
|
|||
func init() { |
|||
// Check if GetFileInformationByHandleEx is available.
|
|||
if procGetFileInformationByHandleEx.Find() != nil { |
|||
procGetFileInformationByHandleEx = nil |
|||
} |
|||
} |
|||
|
|||
// IsTerminal return true if the file descriptor is terminal.
|
|||
func IsTerminal(fd uintptr) bool { |
|||
var st uint32 |
|||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) |
|||
return r != 0 && e == 0 |
|||
} |
|||
|
|||
// Check pipe name is used for cygwin/msys2 pty.
|
|||
// Cygwin/MSYS2 PTY has a name like:
|
|||
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
|||
func isCygwinPipeName(name string) bool { |
|||
token := strings.Split(name, "-") |
|||
if len(token) < 5 { |
|||
return false |
|||
} |
|||
|
|||
if token[0] != `\msys` && |
|||
token[0] != `\cygwin` && |
|||
token[0] != `\Device\NamedPipe\msys` && |
|||
token[0] != `\Device\NamedPipe\cygwin` { |
|||
return false |
|||
} |
|||
|
|||
if token[1] == "" { |
|||
return false |
|||
} |
|||
|
|||
if !strings.HasPrefix(token[2], "pty") { |
|||
return false |
|||
} |
|||
|
|||
if token[3] != `from` && token[3] != `to` { |
|||
return false |
|||
} |
|||
|
|||
if token[4] != "master" { |
|||
return false |
|||
} |
|||
|
|||
return true |
|||
} |
|||
|
|||
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
|
|||
// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
|
|||
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
|
|||
// Windows vista to 10
|
|||
// see https://stackoverflow.com/a/18792477 for details
|
|||
func getFileNameByHandle(fd uintptr) (string, error) { |
|||
if procNtQueryObject == nil { |
|||
return "", errors.New("ntdll.dll: NtQueryObject not supported") |
|||
} |
|||
|
|||
var buf [4 + syscall.MAX_PATH]uint16 |
|||
var result int |
|||
r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, |
|||
fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) |
|||
if r != 0 { |
|||
return "", e |
|||
} |
|||
return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil |
|||
} |
|||
|
|||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
|||
// terminal.
|
|||
func IsCygwinTerminal(fd uintptr) bool { |
|||
if procGetFileInformationByHandleEx == nil { |
|||
name, err := getFileNameByHandle(fd) |
|||
if err != nil { |
|||
return false |
|||
} |
|||
return isCygwinPipeName(name) |
|||
} |
|||
|
|||
// Cygwin/msys's pty is a pipe.
|
|||
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) |
|||
if ft != fileTypePipe || e != 0 { |
|||
return false |
|||
} |
|||
|
|||
var buf [2 + syscall.MAX_PATH]uint16 |
|||
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), |
|||
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), |
|||
uintptr(len(buf)*2), 0, 0) |
|||
if r == 0 || e != 0 { |
|||
return false |
|||
} |
|||
|
|||
l := *(*uint32)(unsafe.Pointer(&buf)) |
|||
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) |
|||
} |
@ -0,0 +1,3 @@ |
|||
# This source code refers to The Go Authors for copyright purposes. |
|||
# The master list of authors is in the main Go distribution, |
|||
# visible at http://tip.golang.org/AUTHORS. |
@ -0,0 +1,3 @@ |
|||
# This source code was written by the Go contributors. |
|||
# The master list of contributors is in the main Go distribution, |
|||
# visible at http://tip.golang.org/CONTRIBUTORS. |
@ -0,0 +1,27 @@ |
|||
Copyright (c) 2009 The Go Authors. All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are |
|||
met: |
|||
|
|||
* Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
* Redistributions in binary form must reproduce the above |
|||
copyright notice, this list of conditions and the following disclaimer |
|||
in the documentation and/or other materials provided with the |
|||
distribution. |
|||
* Neither the name of Google Inc. nor the names of its |
|||
contributors may be used to endorse or promote products derived from |
|||
this software without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,22 @@ |
|||
Additional IP Rights Grant (Patents) |
|||
|
|||
"This implementation" means the copyrightable works distributed by |
|||
Google as part of the Go project. |
|||
|
|||
Google hereby grants to You a perpetual, worldwide, non-exclusive, |
|||
no-charge, royalty-free, irrevocable (except as stated in this section) |
|||
patent license to make, have made, use, offer to sell, sell, import, |
|||
transfer and otherwise run, modify and propagate the contents of this |
|||
implementation of Go, where such license applies only to those patent |
|||
claims, both currently owned or controlled by Google and acquired in |
|||
the future, licensable by Google that are necessarily infringed by this |
|||
implementation of Go. This grant does not include claims that would be |
|||
infringed only as a consequence of further modification of this |
|||
implementation. If you or your agent or exclusive licensee institute or |
|||
order or agree to the institution of patent litigation against any |
|||
entity (including a cross-claim or counterclaim in a lawsuit) alleging |
|||
that this implementation of Go or any code incorporated within this |
|||
implementation of Go constitutes direct or contributory patent |
|||
infringement, or inducement of patent infringement, then any patent |
|||
rights granted to you under this License for this implementation of Go |
|||
shall terminate as of the date such litigation is filed. |
@ -0,0 +1,30 @@ |
|||
// Copyright 2020 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Package unsafeheader contains header declarations for the Go runtime's
|
|||
// slice and string implementations.
|
|||
//
|
|||
// This package allows x/sys to use types equivalent to
|
|||
// reflect.SliceHeader and reflect.StringHeader without introducing
|
|||
// a dependency on the (relatively heavy) "reflect" package.
|
|||
package unsafeheader |
|||
|
|||
import ( |
|||
"unsafe" |
|||
) |
|||
|
|||
// Slice is the runtime representation of a slice.
|
|||
// It cannot be used safely or portably and its representation may change in a later release.
|
|||
type Slice struct { |
|||
Data unsafe.Pointer |
|||
Len int |
|||
Cap int |
|||
} |
|||
|
|||
// String is the runtime representation of a string.
|
|||
// It cannot be used safely or portably and its representation may change in a later release.
|
|||
type String struct { |
|||
Data unsafe.Pointer |
|||
Len int |
|||
} |
@ -0,0 +1,2 @@ |
|||
_obj/ |
|||
unix.test |
@ -0,0 +1,184 @@ |
|||
# Building `sys/unix` |
|||
|
|||
The sys/unix package provides access to the raw system call interface of the |
|||
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix |
|||
|
|||
Porting Go to a new architecture/OS combination or adding syscalls, types, or |
|||
constants to an existing architecture/OS pair requires some manual effort; |
|||
however, there are tools that automate much of the process. |
|||
|
|||
## Build Systems |
|||
|
|||
There are currently two ways we generate the necessary files. We are currently |
|||
migrating the build system to use containers so the builds are reproducible. |
|||
This is being done on an OS-by-OS basis. Please update this documentation as |
|||
components of the build system change. |
|||
|
|||
### Old Build System (currently for `GOOS != "linux"`) |
|||
|
|||
The old build system generates the Go files based on the C header files |
|||
present on your system. This means that files |
|||
for a given GOOS/GOARCH pair must be generated on a system with that OS and |
|||
architecture. This also means that the generated code can differ from system |
|||
to system, based on differences in the header files. |
|||
|
|||
To avoid this, if you are using the old build system, only generate the Go |
|||
files on an installation with unmodified header files. It is also important to |
|||
keep track of which version of the OS the files were generated from (ex. |
|||
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes |
|||
and have each OS upgrade correspond to a single change. |
|||
|
|||
To build the files for your current OS and architecture, make sure GOOS and |
|||
GOARCH are set correctly and run `mkall.sh`. This will generate the files for |
|||
your specific system. Running `mkall.sh -n` shows the commands that will be run. |
|||
|
|||
Requirements: bash, go |
|||
|
|||
### New Build System (currently for `GOOS == "linux"`) |
|||
|
|||
The new build system uses a Docker container to generate the go files directly |
|||
from source checkouts of the kernel and various system libraries. This means |
|||
that on any platform that supports Docker, all the files using the new build |
|||
system can be generated at once, and generated files will not change based on |
|||
what the person running the scripts has installed on their computer. |
|||
|
|||
The OS specific files for the new build system are located in the `${GOOS}` |
|||
directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When |
|||
the kernel or system library updates, modify the Dockerfile at |
|||
`${GOOS}/Dockerfile` to checkout the new release of the source. |
|||
|
|||
To build all the files under the new build system, you must be on an amd64/Linux |
|||
system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will |
|||
then generate all of the files for all of the GOOS/GOARCH pairs in the new build |
|||
system. Running `mkall.sh -n` shows the commands that will be run. |
|||
|
|||
Requirements: bash, go, docker |
|||
|
|||
## Component files |
|||
|
|||
This section describes the various files used in the code generation process. |
|||
It also contains instructions on how to modify these files to add a new |
|||
architecture/OS or to add additional syscalls, types, or constants. Note that |
|||
if you are using the new build system, the scripts/programs cannot be called normally. |
|||
They must be called from within the docker container. |
|||
|
|||
### asm files |
|||
|
|||
The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system |
|||
call dispatch. There are three entry points: |
|||
``` |
|||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) |
|||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) |
|||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) |
|||
``` |
|||
The first and second are the standard ones; they differ only in how many |
|||
arguments can be passed to the kernel. The third is for low-level use by the |
|||
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to |
|||
let it know that a system call is running. |
|||
|
|||
When porting Go to a new architecture/OS, this file must be implemented for |
|||
each GOOS/GOARCH pair. |
|||
|
|||
### mksysnum |
|||
|
|||
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go` |
|||
for the old system). This program takes in a list of header files containing the |
|||
syscall number declarations and parses them to produce the corresponding list of |
|||
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated |
|||
constants. |
|||
|
|||
Adding new syscall numbers is mostly done by running the build on a sufficiently |
|||
new installation of the target OS (or updating the source checkouts for the |
|||
new build system). However, depending on the OS, you may need to update the |
|||
parsing in mksysnum. |
|||
|
|||
### mksyscall.go |
|||
|
|||
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are |
|||
hand-written Go files which implement system calls (for unix, the specific OS, |
|||
or the specific OS/Architecture pair respectively) that need special handling |
|||
and list `//sys` comments giving prototypes for ones that can be generated. |
|||
|
|||
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts |
|||
them into syscalls. This requires the name of the prototype in the comment to |
|||
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function |
|||
prototype can be exported (capitalized) or not. |
|||
|
|||
Adding a new syscall often just requires adding a new `//sys` function prototype |
|||
with the desired arguments and a capitalized name so it is exported. However, if |
|||
you want the interface to the syscall to be different, often one will make an |
|||
unexported `//sys` prototype, and then write a custom wrapper in |
|||
`syscall_${GOOS}.go`. |
|||
|
|||
### types files |
|||
|
|||
For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or |
|||
`types_${GOOS}.go` on the old system). This file includes standard C headers and |
|||
creates Go type aliases to the corresponding C types. The file is then fed |
|||
through godef to get the Go compatible definitions. Finally, the generated code |
|||
is fed though mkpost.go to format the code correctly and remove any hidden or |
|||
private identifiers. This cleaned-up code is written to |
|||
`ztypes_${GOOS}_${GOARCH}.go`. |
|||
|
|||
The hardest part about preparing this file is figuring out which headers to |
|||
include and which symbols need to be `#define`d to get the actual data |
|||
structures that pass through to the kernel system calls. Some C libraries |
|||
preset alternate versions for binary compatibility and translate them on the |
|||
way in and out of system calls, but there is almost always a `#define` that can |
|||
get the real ones. |
|||
See `types_darwin.go` and `linux/types.go` for examples. |
|||
|
|||
To add a new type, add in the necessary include statement at the top of the |
|||
file (if it is not already there) and add in a type alias line. Note that if |
|||
your type is significantly different on different architectures, you may need |
|||
some `#if/#elif` macros in your include statements. |
|||
|
|||
### mkerrors.sh |
|||
|
|||
This script is used to generate the system's various constants. This doesn't |
|||
just include the error numbers and error strings, but also the signal numbers |
|||
and a wide variety of miscellaneous constants. The constants come from the list |
|||
of include files in the `includes_${uname}` variable. A regex then picks out |
|||
the desired `#define` statements, and generates the corresponding Go constants. |
|||
The error numbers and strings are generated from `#include <errno.h>`, and the |
|||
signal numbers and strings are generated from `#include <signal.h>`. All of |
|||
these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program, |
|||
`_errors.c`, which prints out all the constants. |
|||
|
|||
To add a constant, add the header that includes it to the appropriate variable. |
|||
Then, edit the regex (if necessary) to match the desired constant. Avoid making |
|||
the regex too broad to avoid matching unintended constants. |
|||
|
|||
### mkmerge.go |
|||
|
|||
This program is used to extract duplicate const, func, and type declarations |
|||
from the generated architecture-specific files listed below, and merge these |
|||
into a common file for each OS. |
|||
|
|||
The merge is performed in the following steps: |
|||
1. Construct the set of common code that is idential in all architecture-specific files. |
|||
2. Write this common code to the merged file. |
|||
3. Remove the common code from all architecture-specific files. |
|||
|
|||
|
|||
## Generated files |
|||
|
|||
### `zerrors_${GOOS}_${GOARCH}.go` |
|||
|
|||
A file containing all of the system's generated error numbers, error strings, |
|||
signal numbers, and constants. Generated by `mkerrors.sh` (see above). |
|||
|
|||
### `zsyscall_${GOOS}_${GOARCH}.go` |
|||
|
|||
A file containing all the generated syscalls for a specific GOOS and GOARCH. |
|||
Generated by `mksyscall.go` (see above). |
|||
|
|||
### `zsysnum_${GOOS}_${GOARCH}.go` |
|||
|
|||
A list of numeric constants for all the syscall number of the specific GOOS |
|||
and GOARCH. Generated by mksysnum (see above). |
|||
|
|||
### `ztypes_${GOOS}_${GOARCH}.go` |
|||
|
|||
A file containing Go types for passing into (or returning from) syscalls. |
|||
Generated by godefs and the types file (see above). |
@ -0,0 +1,86 @@ |
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// CPU affinity functions
|
|||
|
|||
package unix |
|||
|
|||
import ( |
|||
"math/bits" |
|||
"unsafe" |
|||
) |
|||
|
|||
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS |
|||
|
|||
// CPUSet represents a CPU affinity mask.
|
|||
type CPUSet [cpuSetSize]cpuMask |
|||
|
|||
func schedAffinity(trap uintptr, pid int, set *CPUSet) error { |
|||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set))) |
|||
if e != 0 { |
|||
return errnoErr(e) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
|||
// If pid is 0 the calling thread is used.
|
|||
func SchedGetaffinity(pid int, set *CPUSet) error { |
|||
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set) |
|||
} |
|||
|
|||
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
|||
// If pid is 0 the calling thread is used.
|
|||
func SchedSetaffinity(pid int, set *CPUSet) error { |
|||
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set) |
|||
} |
|||
|
|||
// Zero clears the set s, so that it contains no CPUs.
|
|||
func (s *CPUSet) Zero() { |
|||
for i := range s { |
|||
s[i] = 0 |
|||
} |
|||
} |
|||
|
|||
func cpuBitsIndex(cpu int) int { |
|||
return cpu / _NCPUBITS |
|||
} |
|||
|
|||
func cpuBitsMask(cpu int) cpuMask { |
|||
return cpuMask(1 << (uint(cpu) % _NCPUBITS)) |
|||
} |
|||
|
|||
// Set adds cpu to the set s.
|
|||
func (s *CPUSet) Set(cpu int) { |
|||
i := cpuBitsIndex(cpu) |
|||
if i < len(s) { |
|||
s[i] |= cpuBitsMask(cpu) |
|||
} |
|||
} |
|||
|
|||
// Clear removes cpu from the set s.
|
|||
func (s *CPUSet) Clear(cpu int) { |
|||
i := cpuBitsIndex(cpu) |
|||
if i < len(s) { |
|||
s[i] &^= cpuBitsMask(cpu) |
|||
} |
|||
} |
|||
|
|||
// IsSet reports whether cpu is in the set s.
|
|||
func (s *CPUSet) IsSet(cpu int) bool { |
|||
i := cpuBitsIndex(cpu) |
|||
if i < len(s) { |
|||
return s[i]&cpuBitsMask(cpu) != 0 |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// Count returns the number of CPUs in the set s.
|
|||
func (s *CPUSet) Count() int { |
|||
c := 0 |
|||
for _, b := range s { |
|||
c += bits.OnesCount64(uint64(b)) |
|||
} |
|||
return c |
|||
} |
@ -0,0 +1,15 @@ |
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9
|
|||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
|||
// +build go1.9
|
|||
|
|||
package unix |
|||
|
|||
import "syscall" |
|||
|
|||
type Signal = syscall.Signal |
|||
type Errno = syscall.Errno |
|||
type SysProcAttr = syscall.SysProcAttr |
@ -0,0 +1,18 @@ |
|||
// Copyright 2018 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build gc |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go |
|||
// |
|||
|
|||
TEXT ·syscall6(SB),NOSPLIT,$0-88 |
|||
JMP syscall·syscall6(SB) |
|||
|
|||
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88 |
|||
JMP syscall·rawSyscall6(SB) |
@ -0,0 +1,29 @@ |
|||
// Copyright 2021 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build (freebsd || netbsd || openbsd) && gc |
|||
// +build freebsd netbsd openbsd |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// System call support for 386 BSD |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-28 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 |
|||
JMP syscall·Syscall9(SB) |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
|||
JMP syscall·RawSyscall6(SB) |
@ -0,0 +1,29 @@ |
|||
// Copyright 2021 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc |
|||
// +build darwin dragonfly freebsd netbsd openbsd |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// System call support for AMD64 BSD |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 |
|||
JMP syscall·Syscall9(SB) |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·RawSyscall6(SB) |
@ -0,0 +1,29 @@ |
|||
// Copyright 2021 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build (freebsd || netbsd || openbsd) && gc |
|||
// +build freebsd netbsd openbsd |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// System call support for ARM BSD |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-28 |
|||
B syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 |
|||
B syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 |
|||
B syscall·Syscall9(SB) |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 |
|||
B syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
|||
B syscall·RawSyscall6(SB) |
@ -0,0 +1,29 @@ |
|||
// Copyright 2021 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build (darwin || freebsd || netbsd || openbsd) && gc |
|||
// +build darwin freebsd netbsd openbsd |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// System call support for ARM64 BSD |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 |
|||
JMP syscall·Syscall9(SB) |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·RawSyscall6(SB) |
@ -0,0 +1,66 @@ |
|||
// Copyright 2009 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build gc |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for 386, Linux |
|||
// |
|||
|
|||
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80 |
|||
// instead of the glibc-specific "CALL 0x10(GS)". |
|||
#define INVOKE_SYSCALL INT $0x80 |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-28 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24 |
|||
CALL runtime·entersyscall(SB) |
|||
MOVL trap+0(FP), AX // syscall entry |
|||
MOVL a1+4(FP), BX |
|||
MOVL a2+8(FP), CX |
|||
MOVL a3+12(FP), DX |
|||
MOVL $0, SI |
|||
MOVL $0, DI |
|||
INVOKE_SYSCALL |
|||
MOVL AX, r1+16(FP) |
|||
MOVL DX, r2+20(FP) |
|||
CALL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
|||
JMP syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24 |
|||
MOVL trap+0(FP), AX // syscall entry |
|||
MOVL a1+4(FP), BX |
|||
MOVL a2+8(FP), CX |
|||
MOVL a3+12(FP), DX |
|||
MOVL $0, SI |
|||
MOVL $0, DI |
|||
INVOKE_SYSCALL |
|||
MOVL AX, r1+16(FP) |
|||
MOVL DX, r2+20(FP) |
|||
RET |
|||
|
|||
TEXT ·socketcall(SB),NOSPLIT,$0-36 |
|||
JMP syscall·socketcall(SB) |
|||
|
|||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36 |
|||
JMP syscall·rawsocketcall(SB) |
|||
|
|||
TEXT ·seek(SB),NOSPLIT,$0-28 |
|||
JMP syscall·seek(SB) |
@ -0,0 +1,58 @@ |
|||
// Copyright 2009 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build gc |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for AMD64, Linux |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 |
|||
CALL runtime·entersyscall(SB) |
|||
MOVQ a1+8(FP), DI |
|||
MOVQ a2+16(FP), SI |
|||
MOVQ a3+24(FP), DX |
|||
MOVQ $0, R10 |
|||
MOVQ $0, R8 |
|||
MOVQ $0, R9 |
|||
MOVQ trap+0(FP), AX // syscall entry |
|||
SYSCALL |
|||
MOVQ AX, r1+32(FP) |
|||
MOVQ DX, r2+40(FP) |
|||
CALL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 |
|||
MOVQ a1+8(FP), DI |
|||
MOVQ a2+16(FP), SI |
|||
MOVQ a3+24(FP), DX |
|||
MOVQ $0, R10 |
|||
MOVQ $0, R8 |
|||
MOVQ $0, R9 |
|||
MOVQ trap+0(FP), AX // syscall entry |
|||
SYSCALL |
|||
MOVQ AX, r1+32(FP) |
|||
MOVQ DX, r2+40(FP) |
|||
RET |
|||
|
|||
TEXT ·gettimeofday(SB),NOSPLIT,$0-16 |
|||
JMP syscall·gettimeofday(SB) |
@ -0,0 +1,57 @@ |
|||
// Copyright 2009 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build gc |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for arm, Linux |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-28 |
|||
B syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 |
|||
B syscall·Syscall6(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24 |
|||
BL runtime·entersyscall(SB) |
|||
MOVW trap+0(FP), R7 |
|||
MOVW a1+4(FP), R0 |
|||
MOVW a2+8(FP), R1 |
|||
MOVW a3+12(FP), R2 |
|||
MOVW $0, R3 |
|||
MOVW $0, R4 |
|||
MOVW $0, R5 |
|||
SWI $0 |
|||
MOVW R0, r1+16(FP) |
|||
MOVW $0, R0 |
|||
MOVW R0, r2+20(FP) |
|||
BL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 |
|||
B syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
|||
B syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24 |
|||
MOVW trap+0(FP), R7 // syscall entry |
|||
MOVW a1+4(FP), R0 |
|||
MOVW a2+8(FP), R1 |
|||
MOVW a3+12(FP), R2 |
|||
SWI $0 |
|||
MOVW R0, r1+16(FP) |
|||
MOVW $0, R0 |
|||
MOVW R0, r2+20(FP) |
|||
RET |
|||
|
|||
TEXT ·seek(SB),NOSPLIT,$0-28 |
|||
B syscall·seek(SB) |
@ -0,0 +1,53 @@ |
|||
// Copyright 2015 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build linux && arm64 && gc |
|||
// +build linux |
|||
// +build arm64 |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
B syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
B syscall·Syscall6(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 |
|||
BL runtime·entersyscall(SB) |
|||
MOVD a1+8(FP), R0 |
|||
MOVD a2+16(FP), R1 |
|||
MOVD a3+24(FP), R2 |
|||
MOVD $0, R3 |
|||
MOVD $0, R4 |
|||
MOVD $0, R5 |
|||
MOVD trap+0(FP), R8 // syscall entry |
|||
SVC |
|||
MOVD R0, r1+32(FP) // r1 |
|||
MOVD R1, r2+40(FP) // r2 |
|||
BL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
B syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
B syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 |
|||
MOVD a1+8(FP), R0 |
|||
MOVD a2+16(FP), R1 |
|||
MOVD a3+24(FP), R2 |
|||
MOVD $0, R3 |
|||
MOVD $0, R4 |
|||
MOVD $0, R5 |
|||
MOVD trap+0(FP), R8 // syscall entry |
|||
SVC |
|||
MOVD R0, r1+32(FP) |
|||
MOVD R1, r2+40(FP) |
|||
RET |
@ -0,0 +1,57 @@ |
|||
// Copyright 2015 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build linux && (mips64 || mips64le) && gc |
|||
// +build linux |
|||
// +build mips64 mips64le |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for mips64, Linux |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 |
|||
JAL runtime·entersyscall(SB) |
|||
MOVV a1+8(FP), R4 |
|||
MOVV a2+16(FP), R5 |
|||
MOVV a3+24(FP), R6 |
|||
MOVV R0, R7 |
|||
MOVV R0, R8 |
|||
MOVV R0, R9 |
|||
MOVV trap+0(FP), R2 // syscall entry |
|||
SYSCALL |
|||
MOVV R2, r1+32(FP) |
|||
MOVV R3, r2+40(FP) |
|||
JAL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 |
|||
MOVV a1+8(FP), R4 |
|||
MOVV a2+16(FP), R5 |
|||
MOVV a3+24(FP), R6 |
|||
MOVV R0, R7 |
|||
MOVV R0, R8 |
|||
MOVV R0, R9 |
|||
MOVV trap+0(FP), R2 // syscall entry |
|||
SYSCALL |
|||
MOVV R2, r1+32(FP) |
|||
MOVV R3, r2+40(FP) |
|||
RET |
@ -0,0 +1,55 @@ |
|||
// Copyright 2016 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build linux && (mips || mipsle) && gc |
|||
// +build linux |
|||
// +build mips mipsle |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for mips, Linux |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-28 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-40 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-52 |
|||
JMP syscall·Syscall9(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24 |
|||
JAL runtime·entersyscall(SB) |
|||
MOVW a1+4(FP), R4 |
|||
MOVW a2+8(FP), R5 |
|||
MOVW a3+12(FP), R6 |
|||
MOVW R0, R7 |
|||
MOVW trap+0(FP), R2 // syscall entry |
|||
SYSCALL |
|||
MOVW R2, r1+16(FP) // r1 |
|||
MOVW R3, r2+20(FP) // r2 |
|||
JAL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 |
|||
JMP syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24 |
|||
MOVW a1+4(FP), R4 |
|||
MOVW a2+8(FP), R5 |
|||
MOVW a3+12(FP), R6 |
|||
MOVW trap+0(FP), R2 // syscall entry |
|||
SYSCALL |
|||
MOVW R2, r1+16(FP) |
|||
MOVW R3, r2+20(FP) |
|||
RET |
@ -0,0 +1,45 @@ |
|||
// Copyright 2014 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build linux && (ppc64 || ppc64le) && gc |
|||
// +build linux |
|||
// +build ppc64 ppc64le |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for ppc64, Linux |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 |
|||
BL runtime·entersyscall(SB) |
|||
MOVD a1+8(FP), R3 |
|||
MOVD a2+16(FP), R4 |
|||
MOVD a3+24(FP), R5 |
|||
MOVD R0, R6 |
|||
MOVD R0, R7 |
|||
MOVD R0, R8 |
|||
MOVD trap+0(FP), R9 // syscall entry |
|||
SYSCALL R9 |
|||
MOVD R3, r1+32(FP) |
|||
MOVD R4, r2+40(FP) |
|||
BL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 |
|||
MOVD a1+8(FP), R3 |
|||
MOVD a2+16(FP), R4 |
|||
MOVD a3+24(FP), R5 |
|||
MOVD R0, R6 |
|||
MOVD R0, R7 |
|||
MOVD R0, R8 |
|||
MOVD trap+0(FP), R9 // syscall entry |
|||
SYSCALL R9 |
|||
MOVD R3, r1+32(FP) |
|||
MOVD R4, r2+40(FP) |
|||
RET |
@ -0,0 +1,49 @@ |
|||
// Copyright 2019 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build riscv64 && gc |
|||
// +build riscv64 |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for linux/riscv64. |
|||
// |
|||
// Where available, just jump to package syscall's implementation of |
|||
// these functions. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 |
|||
CALL runtime·entersyscall(SB) |
|||
MOV a1+8(FP), A0 |
|||
MOV a2+16(FP), A1 |
|||
MOV a3+24(FP), A2 |
|||
MOV trap+0(FP), A7 // syscall entry |
|||
ECALL |
|||
MOV A0, r1+32(FP) // r1 |
|||
MOV A1, r2+40(FP) // r2 |
|||
CALL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 |
|||
MOV a1+8(FP), A0 |
|||
MOV a2+16(FP), A1 |
|||
MOV a3+24(FP), A2 |
|||
MOV trap+0(FP), A7 // syscall entry |
|||
ECALL |
|||
MOV A0, r1+32(FP) |
|||
MOV A1, r2+40(FP) |
|||
RET |
@ -0,0 +1,57 @@ |
|||
// Copyright 2016 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build linux && s390x && gc |
|||
// +build linux |
|||
// +build s390x |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for s390x, Linux |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
BR syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
BR syscall·Syscall6(SB) |
|||
|
|||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 |
|||
BL runtime·entersyscall(SB) |
|||
MOVD a1+8(FP), R2 |
|||
MOVD a2+16(FP), R3 |
|||
MOVD a3+24(FP), R4 |
|||
MOVD $0, R5 |
|||
MOVD $0, R6 |
|||
MOVD $0, R7 |
|||
MOVD trap+0(FP), R1 // syscall entry |
|||
SYSCALL |
|||
MOVD R2, r1+32(FP) |
|||
MOVD R3, r2+40(FP) |
|||
BL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
BR syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
BR syscall·RawSyscall6(SB) |
|||
|
|||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 |
|||
MOVD a1+8(FP), R2 |
|||
MOVD a2+16(FP), R3 |
|||
MOVD a3+24(FP), R4 |
|||
MOVD $0, R5 |
|||
MOVD $0, R6 |
|||
MOVD $0, R7 |
|||
MOVD trap+0(FP), R1 // syscall entry |
|||
SYSCALL |
|||
MOVD R2, r1+32(FP) |
|||
MOVD R3, r2+40(FP) |
|||
RET |
@ -0,0 +1,30 @@ |
|||
// Copyright 2019 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build gc |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System call support for mips64, OpenBSD |
|||
// |
|||
|
|||
// Just jump to package syscall's implementation for all these functions. |
|||
// The runtime may know about them. |
|||
|
|||
TEXT ·Syscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·Syscall(SB) |
|||
|
|||
TEXT ·Syscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·Syscall6(SB) |
|||
|
|||
TEXT ·Syscall9(SB),NOSPLIT,$0-104 |
|||
JMP syscall·Syscall9(SB) |
|||
|
|||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56 |
|||
JMP syscall·RawSyscall(SB) |
|||
|
|||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 |
|||
JMP syscall·RawSyscall6(SB) |
@ -0,0 +1,18 @@ |
|||
// Copyright 2014 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build gc |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
// |
|||
// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go |
|||
// |
|||
|
|||
TEXT ·sysvicall6(SB),NOSPLIT,$0-88 |
|||
JMP syscall·sysvicall6(SB) |
|||
|
|||
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88 |
|||
JMP syscall·rawSysvicall6(SB) |
@ -0,0 +1,426 @@ |
|||
// Copyright 2020 The Go Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style |
|||
// license that can be found in the LICENSE file. |
|||
|
|||
//go:build zos && s390x && gc |
|||
// +build zos |
|||
// +build s390x |
|||
// +build gc |
|||
|
|||
#include "textflag.h" |
|||
|
|||
#define PSALAA 1208(R0) |
|||
#define GTAB64(x) 80(x) |
|||
#define LCA64(x) 88(x) |
|||
#define CAA(x) 8(x) |
|||
#define EDCHPXV(x) 1016(x) // in the CAA |
|||
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA |
|||
|
|||
// SS_*, where x=SAVSTACK_ASYNC |
|||
#define SS_LE(x) 0(x) |
|||
#define SS_GO(x) 8(x) |
|||
#define SS_ERRNO(x) 16(x) |
|||
#define SS_ERRNOJR(x) 20(x) |
|||
|
|||
#define LE_CALL BYTE $0x0D; BYTE $0x76; // BL R7, R6 |
|||
|
|||
TEXT ·clearErrno(SB),NOSPLIT,$0-0 |
|||
BL addrerrno<>(SB) |
|||
MOVD $0, 0(R3) |
|||
RET |
|||
|
|||
// Returns the address of errno in R3. |
|||
TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0 |
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get __errno FuncDesc. |
|||
MOVD CAA(R8), R9 |
|||
MOVD EDCHPXV(R9), R9 |
|||
ADD $(0x156*16), R9 |
|||
LMG 0(R9), R5, R6 |
|||
|
|||
// Switch to saved LE stack. |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R4 |
|||
MOVD $0, 0(R9) |
|||
|
|||
// Call __errno function. |
|||
LE_CALL |
|||
NOPH |
|||
|
|||
// Switch back to Go stack. |
|||
XOR R0, R0 // Restore R0 to $0. |
|||
MOVD R4, 0(R9) // Save stack pointer. |
|||
RET |
|||
|
|||
TEXT ·syscall_syscall(SB),NOSPLIT,$0-56 |
|||
BL runtime·entersyscall(SB) |
|||
MOVD a1+8(FP), R1 |
|||
MOVD a2+16(FP), R2 |
|||
MOVD a3+24(FP), R3 |
|||
|
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get function. |
|||
MOVD CAA(R8), R9 |
|||
MOVD EDCHPXV(R9), R9 |
|||
MOVD trap+0(FP), R5 |
|||
SLD $4, R5 |
|||
ADD R5, R9 |
|||
LMG 0(R9), R5, R6 |
|||
|
|||
// Restore LE stack. |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R4 |
|||
MOVD $0, 0(R9) |
|||
|
|||
// Call function. |
|||
LE_CALL |
|||
NOPH |
|||
XOR R0, R0 // Restore R0 to $0. |
|||
MOVD R4, 0(R9) // Save stack pointer. |
|||
|
|||
MOVD R3, r1+32(FP) |
|||
MOVD R0, r2+40(FP) |
|||
MOVD R0, err+48(FP) |
|||
MOVW R3, R4 |
|||
CMP R4, $-1 |
|||
BNE done |
|||
BL addrerrno<>(SB) |
|||
MOVWZ 0(R3), R3 |
|||
MOVD R3, err+48(FP) |
|||
done: |
|||
BL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·syscall_rawsyscall(SB),NOSPLIT,$0-56 |
|||
MOVD a1+8(FP), R1 |
|||
MOVD a2+16(FP), R2 |
|||
MOVD a3+24(FP), R3 |
|||
|
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get function. |
|||
MOVD CAA(R8), R9 |
|||
MOVD EDCHPXV(R9), R9 |
|||
MOVD trap+0(FP), R5 |
|||
SLD $4, R5 |
|||
ADD R5, R9 |
|||
LMG 0(R9), R5, R6 |
|||
|
|||
// Restore LE stack. |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R4 |
|||
MOVD $0, 0(R9) |
|||
|
|||
// Call function. |
|||
LE_CALL |
|||
NOPH |
|||
XOR R0, R0 // Restore R0 to $0. |
|||
MOVD R4, 0(R9) // Save stack pointer. |
|||
|
|||
MOVD R3, r1+32(FP) |
|||
MOVD R0, r2+40(FP) |
|||
MOVD R0, err+48(FP) |
|||
MOVW R3, R4 |
|||
CMP R4, $-1 |
|||
BNE done |
|||
BL addrerrno<>(SB) |
|||
MOVWZ 0(R3), R3 |
|||
MOVD R3, err+48(FP) |
|||
done: |
|||
RET |
|||
|
|||
TEXT ·syscall_syscall6(SB),NOSPLIT,$0-80 |
|||
BL runtime·entersyscall(SB) |
|||
MOVD a1+8(FP), R1 |
|||
MOVD a2+16(FP), R2 |
|||
MOVD a3+24(FP), R3 |
|||
|
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get function. |
|||
MOVD CAA(R8), R9 |
|||
MOVD EDCHPXV(R9), R9 |
|||
MOVD trap+0(FP), R5 |
|||
SLD $4, R5 |
|||
ADD R5, R9 |
|||
LMG 0(R9), R5, R6 |
|||
|
|||
// Restore LE stack. |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R4 |
|||
MOVD $0, 0(R9) |
|||
|
|||
// Fill in parameter list. |
|||
MOVD a4+32(FP), R12 |
|||
MOVD R12, (2176+24)(R4) |
|||
MOVD a5+40(FP), R12 |
|||
MOVD R12, (2176+32)(R4) |
|||
MOVD a6+48(FP), R12 |
|||
MOVD R12, (2176+40)(R4) |
|||
|
|||
// Call function. |
|||
LE_CALL |
|||
NOPH |
|||
XOR R0, R0 // Restore R0 to $0. |
|||
MOVD R4, 0(R9) // Save stack pointer. |
|||
|
|||
MOVD R3, r1+56(FP) |
|||
MOVD R0, r2+64(FP) |
|||
MOVD R0, err+72(FP) |
|||
MOVW R3, R4 |
|||
CMP R4, $-1 |
|||
BNE done |
|||
BL addrerrno<>(SB) |
|||
MOVWZ 0(R3), R3 |
|||
MOVD R3, err+72(FP) |
|||
done: |
|||
BL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·syscall_rawsyscall6(SB),NOSPLIT,$0-80 |
|||
MOVD a1+8(FP), R1 |
|||
MOVD a2+16(FP), R2 |
|||
MOVD a3+24(FP), R3 |
|||
|
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get function. |
|||
MOVD CAA(R8), R9 |
|||
MOVD EDCHPXV(R9), R9 |
|||
MOVD trap+0(FP), R5 |
|||
SLD $4, R5 |
|||
ADD R5, R9 |
|||
LMG 0(R9), R5, R6 |
|||
|
|||
// Restore LE stack. |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R4 |
|||
MOVD $0, 0(R9) |
|||
|
|||
// Fill in parameter list. |
|||
MOVD a4+32(FP), R12 |
|||
MOVD R12, (2176+24)(R4) |
|||
MOVD a5+40(FP), R12 |
|||
MOVD R12, (2176+32)(R4) |
|||
MOVD a6+48(FP), R12 |
|||
MOVD R12, (2176+40)(R4) |
|||
|
|||
// Call function. |
|||
LE_CALL |
|||
NOPH |
|||
XOR R0, R0 // Restore R0 to $0. |
|||
MOVD R4, 0(R9) // Save stack pointer. |
|||
|
|||
MOVD R3, r1+56(FP) |
|||
MOVD R0, r2+64(FP) |
|||
MOVD R0, err+72(FP) |
|||
MOVW R3, R4 |
|||
CMP R4, $-1 |
|||
BNE done |
|||
BL ·rrno<>(SB) |
|||
MOVWZ 0(R3), R3 |
|||
MOVD R3, err+72(FP) |
|||
done: |
|||
RET |
|||
|
|||
TEXT ·syscall_syscall9(SB),NOSPLIT,$0 |
|||
BL runtime·entersyscall(SB) |
|||
MOVD a1+8(FP), R1 |
|||
MOVD a2+16(FP), R2 |
|||
MOVD a3+24(FP), R3 |
|||
|
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get function. |
|||
MOVD CAA(R8), R9 |
|||
MOVD EDCHPXV(R9), R9 |
|||
MOVD trap+0(FP), R5 |
|||
SLD $4, R5 |
|||
ADD R5, R9 |
|||
LMG 0(R9), R5, R6 |
|||
|
|||
// Restore LE stack. |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R4 |
|||
MOVD $0, 0(R9) |
|||
|
|||
// Fill in parameter list. |
|||
MOVD a4+32(FP), R12 |
|||
MOVD R12, (2176+24)(R4) |
|||
MOVD a5+40(FP), R12 |
|||
MOVD R12, (2176+32)(R4) |
|||
MOVD a6+48(FP), R12 |
|||
MOVD R12, (2176+40)(R4) |
|||
MOVD a7+56(FP), R12 |
|||
MOVD R12, (2176+48)(R4) |
|||
MOVD a8+64(FP), R12 |
|||
MOVD R12, (2176+56)(R4) |
|||
MOVD a9+72(FP), R12 |
|||
MOVD R12, (2176+64)(R4) |
|||
|
|||
// Call function. |
|||
LE_CALL |
|||
NOPH |
|||
XOR R0, R0 // Restore R0 to $0. |
|||
MOVD R4, 0(R9) // Save stack pointer. |
|||
|
|||
MOVD R3, r1+80(FP) |
|||
MOVD R0, r2+88(FP) |
|||
MOVD R0, err+96(FP) |
|||
MOVW R3, R4 |
|||
CMP R4, $-1 |
|||
BNE done |
|||
BL addrerrno<>(SB) |
|||
MOVWZ 0(R3), R3 |
|||
MOVD R3, err+96(FP) |
|||
done: |
|||
BL runtime·exitsyscall(SB) |
|||
RET |
|||
|
|||
TEXT ·syscall_rawsyscall9(SB),NOSPLIT,$0 |
|||
MOVD a1+8(FP), R1 |
|||
MOVD a2+16(FP), R2 |
|||
MOVD a3+24(FP), R3 |
|||
|
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get function. |
|||
MOVD CAA(R8), R9 |
|||
MOVD EDCHPXV(R9), R9 |
|||
MOVD trap+0(FP), R5 |
|||
SLD $4, R5 |
|||
ADD R5, R9 |
|||
LMG 0(R9), R5, R6 |
|||
|
|||
// Restore LE stack. |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R4 |
|||
MOVD $0, 0(R9) |
|||
|
|||
// Fill in parameter list. |
|||
MOVD a4+32(FP), R12 |
|||
MOVD R12, (2176+24)(R4) |
|||
MOVD a5+40(FP), R12 |
|||
MOVD R12, (2176+32)(R4) |
|||
MOVD a6+48(FP), R12 |
|||
MOVD R12, (2176+40)(R4) |
|||
MOVD a7+56(FP), R12 |
|||
MOVD R12, (2176+48)(R4) |
|||
MOVD a8+64(FP), R12 |
|||
MOVD R12, (2176+56)(R4) |
|||
MOVD a9+72(FP), R12 |
|||
MOVD R12, (2176+64)(R4) |
|||
|
|||
// Call function. |
|||
LE_CALL |
|||
NOPH |
|||
XOR R0, R0 // Restore R0 to $0. |
|||
MOVD R4, 0(R9) // Save stack pointer. |
|||
|
|||
MOVD R3, r1+80(FP) |
|||
MOVD R0, r2+88(FP) |
|||
MOVD R0, err+96(FP) |
|||
MOVW R3, R4 |
|||
CMP R4, $-1 |
|||
BNE done |
|||
BL addrerrno<>(SB) |
|||
MOVWZ 0(R3), R3 |
|||
MOVD R3, err+96(FP) |
|||
done: |
|||
RET |
|||
|
|||
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64) |
|||
TEXT ·svcCall(SB),NOSPLIT,$0 |
|||
BL runtime·save_g(SB) // Save g and stack pointer |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD R15, 0(R9) |
|||
|
|||
MOVD argv+8(FP), R1 // Move function arguments into registers |
|||
MOVD dsa+16(FP), g |
|||
MOVD fnptr+0(FP), R15 |
|||
|
|||
BYTE $0x0D // Branch to function |
|||
BYTE $0xEF |
|||
|
|||
BL runtime·load_g(SB) // Restore g and stack pointer |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
MOVD SAVSTACK_ASYNC(R8), R9 |
|||
MOVD 0(R9), R15 |
|||
|
|||
RET |
|||
|
|||
// func svcLoad(name *byte) unsafe.Pointer |
|||
TEXT ·svcLoad(SB),NOSPLIT,$0 |
|||
MOVD R15, R2 // Save go stack pointer |
|||
MOVD name+0(FP), R0 // Move SVC args into registers |
|||
MOVD $0x80000000, R1 |
|||
MOVD $0, R15 |
|||
BYTE $0x0A // SVC 08 LOAD |
|||
BYTE $0x08 |
|||
MOVW R15, R3 // Save return code from SVC |
|||
MOVD R2, R15 // Restore go stack pointer |
|||
CMP R3, $0 // Check SVC return code |
|||
BNE error |
|||
|
|||
MOVD $-2, R3 // Reset last bit of entry point to zero |
|||
AND R0, R3 |
|||
MOVD R3, addr+8(FP) // Return entry point returned by SVC |
|||
CMP R0, R3 // Check if last bit of entry point was set |
|||
BNE done |
|||
|
|||
MOVD R15, R2 // Save go stack pointer |
|||
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08) |
|||
BYTE $0x0A // SVC 09 DELETE |
|||
BYTE $0x09 |
|||
MOVD R2, R15 // Restore go stack pointer |
|||
|
|||
error: |
|||
MOVD $0, addr+8(FP) // Return 0 on failure |
|||
done: |
|||
XOR R0, R0 // Reset r0 to 0 |
|||
RET |
|||
|
|||
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64 |
|||
TEXT ·svcUnload(SB),NOSPLIT,$0 |
|||
MOVD R15, R2 // Save go stack pointer |
|||
MOVD name+0(FP), R0 // Move SVC args into registers |
|||
MOVD addr+8(FP), R15 |
|||
BYTE $0x0A // SVC 09 |
|||
BYTE $0x09 |
|||
XOR R0, R0 // Reset r0 to 0 |
|||
MOVD R15, R1 // Save SVC return code |
|||
MOVD R2, R15 // Restore go stack pointer |
|||
MOVD R1, rc+0(FP) // Return SVC return code |
|||
RET |
|||
|
|||
// func gettid() uint64 |
|||
TEXT ·gettid(SB), NOSPLIT, $0 |
|||
// Get library control area (LCA). |
|||
MOVW PSALAA, R8 |
|||
MOVD LCA64(R8), R8 |
|||
|
|||
// Get CEECAATHDID |
|||
MOVD CAA(R8), R9 |
|||
MOVD 0x3D0(R9), R9 |
|||
MOVD R9, ret+0(FP) |
|||
|
|||
RET |
@ -0,0 +1,36 @@ |
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Bluetooth sockets and messages
|
|||
|
|||
package unix |
|||
|
|||
// Bluetooth Protocols
|
|||
const ( |
|||
BTPROTO_L2CAP = 0 |
|||
BTPROTO_HCI = 1 |
|||
BTPROTO_SCO = 2 |
|||
BTPROTO_RFCOMM = 3 |
|||
BTPROTO_BNEP = 4 |
|||
BTPROTO_CMTP = 5 |
|||
BTPROTO_HIDP = 6 |
|||
BTPROTO_AVDTP = 7 |
|||
) |
|||
|
|||
const ( |
|||
HCI_CHANNEL_RAW = 0 |
|||
HCI_CHANNEL_USER = 1 |
|||
HCI_CHANNEL_MONITOR = 2 |
|||
HCI_CHANNEL_CONTROL = 3 |
|||
HCI_CHANNEL_LOGGING = 4 |
|||
) |
|||
|
|||
// Socketoption Level
|
|||
const ( |
|||
SOL_BLUETOOTH = 0x112 |
|||
SOL_HCI = 0x0 |
|||
SOL_L2CAP = 0x6 |
|||
SOL_RFCOMM = 0x12 |
|||
SOL_SCO = 0x11 |
|||
) |
@ -0,0 +1,196 @@ |
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
//go:build freebsd
|
|||
// +build freebsd
|
|||
|
|||
package unix |
|||
|
|||
import ( |
|||
"errors" |
|||
"fmt" |
|||
) |
|||
|
|||
// Go implementation of C mostly found in /usr/src/sys/kern/subr_capability.c
|
|||
|
|||
const ( |
|||
// This is the version of CapRights this package understands. See C implementation for parallels.
|
|||
capRightsGoVersion = CAP_RIGHTS_VERSION_00 |
|||
capArSizeMin = CAP_RIGHTS_VERSION_00 + 2 |
|||
capArSizeMax = capRightsGoVersion + 2 |
|||
) |
|||
|
|||
var ( |
|||
bit2idx = []int{ |
|||
-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, |
|||
4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
} |
|||
) |
|||
|
|||
func capidxbit(right uint64) int { |
|||
return int((right >> 57) & 0x1f) |
|||
} |
|||
|
|||
func rightToIndex(right uint64) (int, error) { |
|||
idx := capidxbit(right) |
|||
if idx < 0 || idx >= len(bit2idx) { |
|||
return -2, fmt.Errorf("index for right 0x%x out of range", right) |
|||
} |
|||
return bit2idx[idx], nil |
|||
} |
|||
|
|||
func caprver(right uint64) int { |
|||
return int(right >> 62) |
|||
} |
|||
|
|||
func capver(rights *CapRights) int { |
|||
return caprver(rights.Rights[0]) |
|||
} |
|||
|
|||
func caparsize(rights *CapRights) int { |
|||
return capver(rights) + 2 |
|||
} |
|||
|
|||
// CapRightsSet sets the permissions in setrights in rights.
|
|||
func CapRightsSet(rights *CapRights, setrights []uint64) error { |
|||
// This is essentially a copy of cap_rights_vset()
|
|||
if capver(rights) != CAP_RIGHTS_VERSION_00 { |
|||
return fmt.Errorf("bad rights version %d", capver(rights)) |
|||
} |
|||
|
|||
n := caparsize(rights) |
|||
if n < capArSizeMin || n > capArSizeMax { |
|||
return errors.New("bad rights size") |
|||
} |
|||
|
|||
for _, right := range setrights { |
|||
if caprver(right) != CAP_RIGHTS_VERSION_00 { |
|||
return errors.New("bad right version") |
|||
} |
|||
i, err := rightToIndex(right) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if i >= n { |
|||
return errors.New("index overflow") |
|||
} |
|||
if capidxbit(rights.Rights[i]) != capidxbit(right) { |
|||
return errors.New("index mismatch") |
|||
} |
|||
rights.Rights[i] |= right |
|||
if capidxbit(rights.Rights[i]) != capidxbit(right) { |
|||
return errors.New("index mismatch (after assign)") |
|||
} |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// CapRightsClear clears the permissions in clearrights from rights.
|
|||
func CapRightsClear(rights *CapRights, clearrights []uint64) error { |
|||
// This is essentially a copy of cap_rights_vclear()
|
|||
if capver(rights) != CAP_RIGHTS_VERSION_00 { |
|||
return fmt.Errorf("bad rights version %d", capver(rights)) |
|||
} |
|||
|
|||
n := caparsize(rights) |
|||
if n < capArSizeMin || n > capArSizeMax { |
|||
return errors.New("bad rights size") |
|||
} |
|||
|
|||
for _, right := range clearrights { |
|||
if caprver(right) != CAP_RIGHTS_VERSION_00 { |
|||
return errors.New("bad right version") |
|||
} |
|||
i, err := rightToIndex(right) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if i >= n { |
|||
return errors.New("index overflow") |
|||
} |
|||
if capidxbit(rights.Rights[i]) != capidxbit(right) { |
|||
return errors.New("index mismatch") |
|||
} |
|||
rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF) |
|||
if capidxbit(rights.Rights[i]) != capidxbit(right) { |
|||
return errors.New("index mismatch (after assign)") |
|||
} |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// CapRightsIsSet checks whether all the permissions in setrights are present in rights.
|
|||
func CapRightsIsSet(rights *CapRights, setrights []uint64) (bool, error) { |
|||
// This is essentially a copy of cap_rights_is_vset()
|
|||
if capver(rights) != CAP_RIGHTS_VERSION_00 { |
|||
return false, fmt.Errorf("bad rights version %d", capver(rights)) |
|||
} |
|||
|
|||
n := caparsize(rights) |
|||
if n < capArSizeMin || n > capArSizeMax { |
|||
return false, errors.New("bad rights size") |
|||
} |
|||
|
|||
for _, right := range setrights { |
|||
if caprver(right) != CAP_RIGHTS_VERSION_00 { |
|||
return false, errors.New("bad right version") |
|||
} |
|||
i, err := rightToIndex(right) |
|||
if err != nil { |
|||
return false, err |
|||
} |
|||
if i >= n { |
|||
return false, errors.New("index overflow") |
|||
} |
|||
if capidxbit(rights.Rights[i]) != capidxbit(right) { |
|||
return false, errors.New("index mismatch") |
|||
} |
|||
if (rights.Rights[i] & right) != right { |
|||
return false, nil |
|||
} |
|||
} |
|||
|
|||
return true, nil |
|||
} |
|||
|
|||
func capright(idx uint64, bit uint64) uint64 { |
|||
return ((1 << (57 + idx)) | bit) |
|||
} |
|||
|
|||
// CapRightsInit returns a pointer to an initialised CapRights structure filled with rights.
|
|||
// See man cap_rights_init(3) and rights(4).
|
|||
func CapRightsInit(rights []uint64) (*CapRights, error) { |
|||
var r CapRights |
|||
r.Rights[0] = (capRightsGoVersion << 62) | capright(0, 0) |
|||
r.Rights[1] = capright(1, 0) |
|||
|
|||
err := CapRightsSet(&r, rights) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return &r, nil |
|||
} |
|||
|
|||
// CapRightsLimit reduces the operations permitted on fd to at most those contained in rights.
|
|||
// The capability rights on fd can never be increased by CapRightsLimit.
|
|||
// See man cap_rights_limit(2) and rights(4).
|
|||
func CapRightsLimit(fd uintptr, rights *CapRights) error { |
|||
return capRightsLimit(int(fd), rights) |
|||
} |
|||
|
|||
// CapRightsGet returns a CapRights structure containing the operations permitted on fd.
|
|||
// See man cap_rights_get(3) and rights(4).
|
|||
func CapRightsGet(fd uintptr) (*CapRights, error) { |
|||
r, err := CapRightsInit(nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
err = capRightsGet(capRightsGoVersion, int(fd), r) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return r, nil |
|||
} |
@ -0,0 +1,14 @@ |
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
|||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
|||
|
|||
package unix |
|||
|
|||
const ( |
|||
R_OK = 0x4 |
|||
W_OK = 0x2 |
|||
X_OK = 0x1 |
|||
) |
@ -0,0 +1,27 @@ |
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
//go:build aix && ppc
|
|||
// +build aix,ppc
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used by AIX.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a Linux device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev >> 16) & 0xffff) |
|||
} |
|||
|
|||
// Minor returns the minor component of a Linux device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
return uint32(dev & 0xffff) |
|||
} |
|||
|
|||
// Mkdev returns a Linux device number generated from the given major and minor
|
|||
// components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
return uint64(((major) << 16) | (minor)) |
|||
} |
@ -0,0 +1,29 @@ |
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
//go:build aix && ppc64
|
|||
// +build aix,ppc64
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used AIX.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a Linux device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev & 0x3fffffff00000000) >> 32) |
|||
} |
|||
|
|||
// Minor returns the minor component of a Linux device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
return uint32((dev & 0x00000000ffffffff) >> 0) |
|||
} |
|||
|
|||
// Mkdev returns a Linux device number generated from the given major and minor
|
|||
// components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
var DEVNO64 uint64 |
|||
DEVNO64 = 0x8000000000000000 |
|||
return ((uint64(major) << 32) | (uint64(minor) & 0x00000000FFFFFFFF) | DEVNO64) |
|||
} |
@ -0,0 +1,24 @@ |
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used in Darwin's sys/types.h header.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a Darwin device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev >> 24) & 0xff) |
|||
} |
|||
|
|||
// Minor returns the minor component of a Darwin device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
return uint32(dev & 0xffffff) |
|||
} |
|||
|
|||
// Mkdev returns a Darwin device number generated from the given major and minor
|
|||
// components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
return (uint64(major) << 24) | uint64(minor) |
|||
} |
@ -0,0 +1,30 @@ |
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used in Dragonfly's sys/types.h header.
|
|||
//
|
|||
// The information below is extracted and adapted from sys/types.h:
|
|||
//
|
|||
// Minor gives a cookie instead of an index since in order to avoid changing the
|
|||
// meanings of bits 0-15 or wasting time and space shifting bits 16-31 for
|
|||
// devices that don't use them.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a DragonFlyBSD device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev >> 8) & 0xff) |
|||
} |
|||
|
|||
// Minor returns the minor component of a DragonFlyBSD device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
return uint32(dev & 0xffff00ff) |
|||
} |
|||
|
|||
// Mkdev returns a DragonFlyBSD device number generated from the given major and
|
|||
// minor components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
return (uint64(major) << 8) | uint64(minor) |
|||
} |
@ -0,0 +1,30 @@ |
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used in FreeBSD's sys/types.h header.
|
|||
//
|
|||
// The information below is extracted and adapted from sys/types.h:
|
|||
//
|
|||
// Minor gives a cookie instead of an index since in order to avoid changing the
|
|||
// meanings of bits 0-15 or wasting time and space shifting bits 16-31 for
|
|||
// devices that don't use them.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a FreeBSD device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev >> 8) & 0xff) |
|||
} |
|||
|
|||
// Minor returns the minor component of a FreeBSD device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
return uint32(dev & 0xffff00ff) |
|||
} |
|||
|
|||
// Mkdev returns a FreeBSD device number generated from the given major and
|
|||
// minor components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
return (uint64(major) << 8) | uint64(minor) |
|||
} |
@ -0,0 +1,42 @@ |
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used by the Linux kernel and glibc.
|
|||
//
|
|||
// The information below is extracted and adapted from bits/sysmacros.h in the
|
|||
// glibc sources:
|
|||
//
|
|||
// dev_t in glibc is 64-bit, with 32-bit major and minor numbers. glibc's
|
|||
// default encoding is MMMM Mmmm mmmM MMmm, where M is a hex digit of the major
|
|||
// number and m is a hex digit of the minor number. This is backward compatible
|
|||
// with legacy systems where dev_t is 16 bits wide, encoded as MMmm. It is also
|
|||
// backward compatible with the Linux kernel, which for some architectures uses
|
|||
// 32-bit dev_t, encoded as mmmM MMmm.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a Linux device number.
|
|||
func Major(dev uint64) uint32 { |
|||
major := uint32((dev & 0x00000000000fff00) >> 8) |
|||
major |= uint32((dev & 0xfffff00000000000) >> 32) |
|||
return major |
|||
} |
|||
|
|||
// Minor returns the minor component of a Linux device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
minor := uint32((dev & 0x00000000000000ff) >> 0) |
|||
minor |= uint32((dev & 0x00000ffffff00000) >> 12) |
|||
return minor |
|||
} |
|||
|
|||
// Mkdev returns a Linux device number generated from the given major and minor
|
|||
// components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
dev := (uint64(major) & 0x00000fff) << 8 |
|||
dev |= (uint64(major) & 0xfffff000) << 32 |
|||
dev |= (uint64(minor) & 0x000000ff) << 0 |
|||
dev |= (uint64(minor) & 0xffffff00) << 12 |
|||
return dev |
|||
} |
@ -0,0 +1,29 @@ |
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used in NetBSD's sys/types.h header.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a NetBSD device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev & 0x000fff00) >> 8) |
|||
} |
|||
|
|||
// Minor returns the minor component of a NetBSD device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
minor := uint32((dev & 0x000000ff) >> 0) |
|||
minor |= uint32((dev & 0xfff00000) >> 12) |
|||
return minor |
|||
} |
|||
|
|||
// Mkdev returns a NetBSD device number generated from the given major and minor
|
|||
// components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
dev := (uint64(major) << 8) & 0x000fff00 |
|||
dev |= (uint64(minor) << 12) & 0xfff00000 |
|||
dev |= (uint64(minor) << 0) & 0x000000ff |
|||
return dev |
|||
} |
@ -0,0 +1,29 @@ |
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used in OpenBSD's sys/types.h header.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of an OpenBSD device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev & 0x0000ff00) >> 8) |
|||
} |
|||
|
|||
// Minor returns the minor component of an OpenBSD device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
minor := uint32((dev & 0x000000ff) >> 0) |
|||
minor |= uint32((dev & 0xffff0000) >> 8) |
|||
return minor |
|||
} |
|||
|
|||
// Mkdev returns an OpenBSD device number generated from the given major and minor
|
|||
// components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
dev := (uint64(major) << 8) & 0x0000ff00 |
|||
dev |= (uint64(minor) << 8) & 0xffff0000 |
|||
dev |= (uint64(minor) << 0) & 0x000000ff |
|||
return dev |
|||
} |
@ -0,0 +1,29 @@ |
|||
// Copyright 2020 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
//go:build zos && s390x
|
|||
// +build zos,s390x
|
|||
|
|||
// Functions to access/create device major and minor numbers matching the
|
|||
// encoding used by z/OS.
|
|||
//
|
|||
// The information below is extracted and adapted from <sys/stat.h> macros.
|
|||
|
|||
package unix |
|||
|
|||
// Major returns the major component of a z/OS device number.
|
|||
func Major(dev uint64) uint32 { |
|||
return uint32((dev >> 16) & 0x0000FFFF) |
|||
} |
|||
|
|||
// Minor returns the minor component of a z/OS device number.
|
|||
func Minor(dev uint64) uint32 { |
|||
return uint32(dev & 0x0000FFFF) |
|||
} |
|||
|
|||
// Mkdev returns a z/OS device number generated from the given major and minor
|
|||
// components.
|
|||
func Mkdev(major, minor uint32) uint64 { |
|||
return (uint64(major) << 16) | uint64(minor) |
|||
} |
@ -0,0 +1,103 @@ |
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
|||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
|||
|
|||
package unix |
|||
|
|||
import "unsafe" |
|||
|
|||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
|||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) { |
|||
if len(b) < int(off+size) { |
|||
return 0, false |
|||
} |
|||
if isBigEndian { |
|||
return readIntBE(b[off:], size), true |
|||
} |
|||
return readIntLE(b[off:], size), true |
|||
} |
|||
|
|||
func readIntBE(b []byte, size uintptr) uint64 { |
|||
switch size { |
|||
case 1: |
|||
return uint64(b[0]) |
|||
case 2: |
|||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
|||
return uint64(b[1]) | uint64(b[0])<<8 |
|||
case 4: |
|||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
|||
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24 |
|||
case 8: |
|||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
|||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | |
|||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 |
|||
default: |
|||
panic("syscall: readInt with unsupported size") |
|||
} |
|||
} |
|||
|
|||
func readIntLE(b []byte, size uintptr) uint64 { |
|||
switch size { |
|||
case 1: |
|||
return uint64(b[0]) |
|||
case 2: |
|||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
|||
return uint64(b[0]) | uint64(b[1])<<8 |
|||
case 4: |
|||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
|||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|||
case 8: |
|||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
|||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | |
|||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 |
|||
default: |
|||
panic("syscall: readInt with unsupported size") |
|||
} |
|||
} |
|||
|
|||
// ParseDirent parses up to max directory entries in buf,
|
|||
// appending the names to names. It returns the number of
|
|||
// bytes consumed from buf, the number of entries added
|
|||
// to names, and the new names slice.
|
|||
func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) { |
|||
origlen := len(buf) |
|||
count = 0 |
|||
for max != 0 && len(buf) > 0 { |
|||
reclen, ok := direntReclen(buf) |
|||
if !ok || reclen > uint64(len(buf)) { |
|||
return origlen, count, names |
|||
} |
|||
rec := buf[:reclen] |
|||
buf = buf[reclen:] |
|||
ino, ok := direntIno(rec) |
|||
if !ok { |
|||
break |
|||
} |
|||
if ino == 0 { // File absent in directory.
|
|||
continue |
|||
} |
|||
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name)) |
|||
namlen, ok := direntNamlen(rec) |
|||
if !ok || namoff+namlen > uint64(len(rec)) { |
|||
break |
|||
} |
|||
name := rec[namoff : namoff+namlen] |
|||
for i, c := range name { |
|||
if c == 0 { |
|||
name = name[:i] |
|||
break |
|||
} |
|||
} |
|||
// Check for useless names before allocating a string.
|
|||
if string(name) == "." || string(name) == ".." { |
|||
continue |
|||
} |
|||
max-- |
|||
count++ |
|||
names = append(names, string(name)) |
|||
} |
|||
return origlen - len(buf), count, names |
|||
} |
@ -0,0 +1,10 @@ |
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
//
|
|||
//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64
|
|||
// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64
|
|||
|
|||
package unix |
|||
|
|||
const isBigEndian = true |
@ -0,0 +1,10 @@ |
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
//
|
|||
//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh
|
|||
// +build 386 amd64 amd64p32 alpha arm arm64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh
|
|||
|
|||
package unix |
|||
|
|||
const isBigEndian = false |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue