Skip to content

Commit

Permalink
Merge pull request #116
Browse files Browse the repository at this point in the history
Release v0.5.0
  • Loading branch information
bsrinivas8687 authored Feb 12, 2023
2 parents ad9ad3d + 70fc3ef commit cbdb2dc
Show file tree
Hide file tree
Showing 35 changed files with 1,372 additions and 487 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ FROM alpine:3.17
COPY --from=build /go/bin/sentinelnode /usr/local/bin/process
COPY --from=build /root/hnsd/hnsd /usr/local/bin/hnsd

RUN apk add --no-cache ip6tables unbound-dev wireguard-tools && \
RUN apk add --no-cache ip6tables unbound-dev v2ray wireguard-tools && \
rm -rf /tmp/* /var/tmp/*

CMD ["process"]
14 changes: 14 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package api

import (
"github.com/gin-gonic/gin"

"github.com/sentinel-official/dvpn-node/api/session"
"github.com/sentinel-official/dvpn-node/api/status"
"github.com/sentinel-official/dvpn-node/context"
)

func RegisterRoutes(ctx *context.Context, r gin.IRouter) {
session.RegisterRoutes(ctx, r)
status.RegisterRoutes(ctx, r)
}
226 changes: 226 additions & 0 deletions api/session/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package session

import (
"encoding/base64"
"fmt"
"net"
"net/http"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/gin-gonic/gin"
hubtypes "github.com/sentinel-official/hub/types"
"github.com/v2fly/v2ray-core/v5/common/uuid"

"github.com/sentinel-official/dvpn-node/context"
v2raytypes "github.com/sentinel-official/dvpn-node/services/v2ray/types"
"github.com/sentinel-official/dvpn-node/types"
)

func HandlerAddSession(ctx *context.Context) gin.HandlerFunc {
return func(c *gin.Context) {
if ctx.Service().PeersCount() >= ctx.Config().QOS.MaxPeers {
err := fmt.Errorf("reached maximum peers limit; maximum %d", ctx.Config().QOS.MaxPeers)
c.JSON(http.StatusBadRequest, types.NewResponseError(1, err))
return
}

req, err := NewRequestAddSession(c)
if err != nil {
c.JSON(http.StatusBadRequest, types.NewResponseError(2, err))
return
}

key := base64.StdEncoding.EncodeToString(req.key)
if ctx.Service().Type() == v2raytypes.Type {
uid, err := uuid.ParseBytes(req.key[1:])
if err != nil {
c.JSON(http.StatusBadRequest, types.NewResponseError(2, err))
return
}

key = uid.String()
}

item := types.Session{}
ctx.Database().Model(
&types.Session{},
).Where(
&types.Session{
ID: req.id,
},
).First(&item)

if item.ID != 0 {
err = fmt.Errorf("peer for session %d already exist", req.id)
c.JSON(http.StatusBadRequest, types.NewResponseError(3, err))
return
}

item = types.Session{}
ctx.Database().Model(
&types.Session{},
).Where(
&types.Session{
Key: key,
},
).First(&item)

if item.ID != 0 {
err = fmt.Errorf("key %s for service already exist", key)
c.JSON(http.StatusBadRequest, types.NewResponseError(3, err))
return
}

account, err := ctx.Client().QueryAccount(req.accAddress)
if err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(4, err))
return
}
if account == nil {
err = fmt.Errorf("account %s does not exist", req.accAddress)
c.JSON(http.StatusNotFound, types.NewResponseError(4, err))
return
}
if account.GetPubKey() == nil {
err = fmt.Errorf("public key for account %s does not exist", req.accAddress)
c.JSON(http.StatusNotFound, types.NewResponseError(4, err))
return
}
if ok := account.GetPubKey().VerifySignature(sdk.Uint64ToBigEndian(req.id), req.signature); !ok {
err = fmt.Errorf("failed to verify the signature %s", req.signature)
c.JSON(http.StatusBadRequest, types.NewResponseError(4, err))
return
}

session, err := ctx.Client().QuerySession(req.id)
if err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(5, err))
return
}
if session == nil {
err = fmt.Errorf("session %d does not exist", req.id)
c.JSON(http.StatusNotFound, types.NewResponseError(5, err))
return
}
if !session.Status.Equal(hubtypes.StatusActive) {
err = fmt.Errorf("invalid status for session %d; expected %s, got %s", session.Id, hubtypes.StatusActive, session.Status)
c.JSON(http.StatusNotFound, types.NewResponseError(5, err))
return
}
if session.Address != req.URI.AccAddress {
err = fmt.Errorf("account address mismatch; expected %s, got %s", req.URI.AccAddress, session.Address)
c.JSON(http.StatusBadRequest, types.NewResponseError(5, err))
return
}

subscription, err := ctx.Client().QuerySubscription(session.Subscription)
if err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(6, err))
return
}
if subscription == nil {
err = fmt.Errorf("subscription %d does not exist", session.Subscription)
c.JSON(http.StatusNotFound, types.NewResponseError(6, err))
return
}
if !subscription.Status.Equal(hubtypes.StatusActive) {
err = fmt.Errorf("invalid status for subscription %d; expected %s, got %s", subscription.Id, hubtypes.StatusActive, subscription.Status)
c.JSON(http.StatusBadRequest, types.NewResponseError(6, err))
return
}

if subscription.Plan == 0 {
if subscription.Node != ctx.Address().String() {
err = fmt.Errorf("node address mismatch; expected %s, got %s", ctx.Address(), subscription.Node)
c.JSON(http.StatusBadRequest, types.NewResponseError(7, err))
return
}
} else {
ok, err := ctx.Client().HasNodeForPlan(subscription.Plan, ctx.Address())
if err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(7, err))
return
}
if !ok {
err = fmt.Errorf("node %s does not exist for plan %d", ctx.Address(), subscription.Plan)
c.JSON(http.StatusBadRequest, types.NewResponseError(7, err))
return
}
}

quota, err := ctx.Client().QueryQuota(subscription.Id, req.accAddress)
if err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(8, err))
return
}
if quota == nil {
err = fmt.Errorf("quota for address %s does not exist", req.URI.AccAddress)
c.JSON(http.StatusNotFound, types.NewResponseError(8, err))
return
}

var items []types.Session
ctx.Database().Model(
&types.Session{},
).Where(
&types.Session{
Subscription: subscription.Id,
Address: req.URI.AccAddress,
},
).Find(&items)

for i := 0; i < len(items); i++ {
session, err := ctx.Client().QuerySession(items[i].ID)
if err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(9, err))
return
}
if session == nil {
continue
}

if err = ctx.RemovePeer(items[i].Key); err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(9, err))
return
}

consumed := items[i].Download + items[i].Upload
quota.Consumed = quota.Consumed.Add(
hubtypes.NewBandwidthFromInt64(
consumed, 0,
).CeilTo(
hubtypes.Gigabyte.Quo(subscription.Price.Amount),
).Sum(),
)
}

if quota.Consumed.GTE(quota.Allocated) {
err := fmt.Errorf("quota exceeded; allocated %s, consumed %s", quota.Allocated, quota.Consumed)
c.JSON(http.StatusBadRequest, types.NewResponseError(10, err))
return
}

result, err := ctx.Service().AddPeer(req.key)
if err != nil {
c.JSON(http.StatusInternalServerError, types.NewResponseError(11, err))
return
}
ctx.Log().Info("Added a new peer", "key", key, "count", ctx.Service().PeersCount())

ctx.Database().Model(
&types.Session{},
).Create(
&types.Session{
ID: req.id,
Subscription: subscription.Id,
Key: key,
Address: req.URI.AccAddress,
Available: quota.Allocated.Sub(quota.Consumed).Int64(),
},
)

result = append(result, net.ParseIP(ctx.Location().IP).To4()...)
result = append(result, ctx.Service().Info()...)
c.JSON(http.StatusCreated, types.NewResponseResult(result))
}
}
52 changes: 52 additions & 0 deletions api/session/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package session

import (
"encoding/base64"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/gin-gonic/gin"
)

type RequestAddSession struct {
accAddress sdk.AccAddress
id uint64
key []byte
signature []byte

URI struct {
AccAddress string `uri:"acc_address"`
ID uint64 `uri:"id" binding:"gt=0"`
}
Body struct {
Key string `json:"key"`
Signature string `json:"signature"`
}
}

func NewRequestAddSession(c *gin.Context) (req *RequestAddSession, err error) {
req = &RequestAddSession{}
if err = c.ShouldBindUri(&req.URI); err != nil {
return nil, err
}
if err = c.ShouldBindJSON(&req.Body); err != nil {
return nil, err
}

req.accAddress, err = sdk.AccAddressFromBech32(req.URI.AccAddress)
if err != nil {
return nil, err
}

req.id = req.URI.ID

req.key, err = base64.StdEncoding.DecodeString(req.Body.Key)
if err != nil {
return nil, err
}
req.signature, err = base64.StdEncoding.DecodeString(req.Body.Signature)
if err != nil {
return nil, err
}

return req, nil
}
11 changes: 11 additions & 0 deletions api/session/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package session

import (
"github.com/gin-gonic/gin"

"github.com/sentinel-official/dvpn-node/context"
)

func RegisterRoutes(ctx *context.Context, router gin.IRouter) {
router.POST("/accounts/:acc_address/sessions/:id", HandlerAddSession(ctx))
}
13 changes: 8 additions & 5 deletions rest/status/handlers.go → api/status/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"net/http"

"github.com/cosmos/cosmos-sdk/version"
"github.com/gin-gonic/gin"

"github.com/sentinel-official/dvpn-node/context"
"github.com/sentinel-official/dvpn-node/utils"
"github.com/sentinel-official/dvpn-node/types"
)

func HandlerGetStatus(ctx *context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
utils.WriteResultToResponse(w, http.StatusOK, &ResponseGetStatus{
func HandlerGetStatus(ctx *context.Context) gin.HandlerFunc {
return func(c *gin.Context) {
item := &ResponseGetStatus{
Address: ctx.Address().String(),
Bandwidth: &Bandwidth{
Upload: ctx.Bandwidth().Upload.Int64(),
Expand Down Expand Up @@ -40,6 +41,8 @@ func HandlerGetStatus(ctx *context.Context) http.HandlerFunc {
},
Type: ctx.Service().Type(),
Version: version.Version,
})
}

c.JSON(http.StatusOK, types.NewResponseResult(item))
}
}
3 changes: 0 additions & 3 deletions rest/status/responses.go → api/status/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ type (
QOS struct {
MaxPeers int `json:"max_peers"`
}
)

type (
ResponseGetStatus struct {
Address string `json:"address"`
Bandwidth *Bandwidth `json:"bandwidth"`
Expand Down
11 changes: 11 additions & 0 deletions api/status/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package status

import (
"github.com/gin-gonic/gin"

"github.com/sentinel-official/dvpn-node/context"
)

func RegisterRoutes(ctx *context.Context, r gin.IRouter) {
r.GET("/status", HandlerGetStatus(ctx))
}
8 changes: 4 additions & 4 deletions cmd/flags.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package cmd

const (
flagAccount = "account"
flagIndex = "index"
flagRecover = "recover"
flagEnableConfigValidation = "enable-config-validation"
flagAccount = "account"
flagIndex = "index"
flagRecover = "recover"
flagSkipConfigValidation = "skip-config-validation"
)
Loading

0 comments on commit cbdb2dc

Please sign in to comment.