2020-05-04 14:58:02 +00:00
// Copyright or © or Copr. happyDNS (2020)
//
// contact@happydns.org
//
// This software is a computer program whose purpose is to provide a modern
// interface to interact with DNS systems.
//
// This software is governed by the CeCILL license under French law and abiding
// by the rules of distribution of free software. You can use, modify and/or
// redistribute the software under the terms of the CeCILL license as
// circulated by CEA, CNRS and INRIA at the following URL
// "http://www.cecill.info".
//
// As a counterpart to the access to the source code and rights to copy, modify
// and redistribute granted by the license, users are provided only with a
// limited warranty and the software's author, the holder of the economic
// rights, and the successive licensors have only limited liability.
//
// In this respect, the user's attention is drawn to the risks associated with
// loading, using, modifying and/or developing or reproducing the software by
// the user in light of its specific status of free software, that may mean
// that it is complicated to manipulate, and that also therefore means that it
// is reserved for developers and experienced professionals having in-depth
// computer knowledge. Users are therefore encouraged to load and test the
// software's suitability as regards their requirements in conditions enabling
// the security of their systems and/or data to be ensured and, more generally,
// to use and operate it in the same conditions as regards security.
//
// The fact that you are presently reading this means that you have had
// knowledge of the CeCILL license and that you accept its terms.
2020-04-22 08:17:16 +00:00
package api
import (
"fmt"
2021-05-05 01:48:16 +00:00
"log"
2020-04-22 08:17:16 +00:00
"net/http"
2021-05-05 01:48:16 +00:00
"github.com/gin-gonic/gin"
2020-04-22 08:17:16 +00:00
"github.com/miekg/dns"
"git.happydns.org/happydns/config"
"git.happydns.org/happydns/model"
"git.happydns.org/happydns/storage"
)
2021-05-05 01:48:16 +00:00
func declareDomainsRoutes ( cfg * config . Options , router * gin . RouterGroup ) {
router . GET ( "/domains" , GetDomains )
router . POST ( "/domains" , addDomain )
2020-05-09 11:14:29 +00:00
2021-05-05 01:48:16 +00:00
apiDomainsRoutes := router . Group ( "/domains/:domain" )
apiDomainsRoutes . Use ( DomainHandler )
2020-05-09 11:14:29 +00:00
2021-05-05 01:48:16 +00:00
apiDomainsRoutes . GET ( "" , GetDomain )
apiDomainsRoutes . DELETE ( "" , delDomain )
apiDomainsRoutes . GET ( "/rr" , axfrDomain )
apiDomainsRoutes . POST ( "/rr" , prepareRR ( addRR ) )
apiDomainsRoutes . DELETE ( "/rr" , prepareRR ( delRR ) )
declareZonesRoutes ( cfg , apiDomainsRoutes )
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
func GetDomains ( c * gin . Context ) {
user := myUser ( c )
if user == nil {
c . AbortWithStatusJSON ( http . StatusUnauthorized , gin . H { "errmsg" : "User not defined" } )
return
}
if domains , err := storage . MainStore . GetDomains ( user ) ; err != nil {
log . Printf ( "%s: An error occurs when trying to GetDomains: %w" , c . ClientIP ( ) , err )
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : err } )
2020-05-05 15:24:54 +00:00
} else if len ( domains ) > 0 {
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , domains )
2020-05-05 15:24:54 +00:00
} else {
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , [ ] happydns . Domain { } )
2020-04-22 08:17:16 +00:00
}
}
2021-05-05 01:48:16 +00:00
func addDomain ( c * gin . Context ) {
2020-04-22 08:17:16 +00:00
var uz happydns . Domain
2021-05-05 01:48:16 +00:00
err := c . ShouldBindJSON ( & uz )
2020-04-22 08:17:16 +00:00
if err != nil {
2021-05-05 01:48:16 +00:00
log . Printf ( "%s sends invalid Domain JSON: %w" , c . ClientIP ( ) , err )
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : fmt . Sprintf ( "Something is wrong in received data: %w" , err ) } )
return
2020-04-22 08:17:16 +00:00
}
if len ( uz . DomainName ) <= 2 {
2021-05-05 01:48:16 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "The given domain is invalid." } )
return
2020-04-22 08:17:16 +00:00
}
2020-05-02 08:55:08 +00:00
uz . DomainName = dns . Fqdn ( uz . DomainName )
if _ , ok := dns . IsDomainName ( uz . DomainName ) ; ! ok {
2021-05-05 01:48:16 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : fmt . Sprintf ( "%q is not a valid domain name." , uz . DomainName ) } )
return
2020-05-02 08:55:08 +00:00
}
2021-05-05 01:48:16 +00:00
user := c . MustGet ( "LoggedUser" ) . ( * happydns . User )
2021-05-21 11:08:18 +00:00
provider , err := storage . MainStore . GetProvider ( user , uz . IdProvider )
2020-05-02 08:55:08 +00:00
if err != nil {
2021-05-05 01:48:16 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : fmt . Sprintf ( "Unable to find the provider." ) } )
return
2020-04-22 08:17:16 +00:00
}
if storage . MainStore . DomainExists ( uz . DomainName ) {
2021-05-05 01:48:16 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : "This domain has already been imported." } )
return
2020-05-02 08:55:08 +00:00
2021-05-21 11:08:18 +00:00
} else if err := provider . DomainExists ( uz . DomainName ) ; err != nil {
2021-05-05 01:48:16 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
} else if err := storage . MainStore . CreateDomain ( user , & uz ) ; err != nil {
log . Printf ( "%s was unable to CreateDomain: %w" , c . ClientIP ( ) , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : "Sorry, we are unable to create your domain now." } )
return
2020-04-22 08:17:16 +00:00
} else {
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , uz )
2020-04-22 08:17:16 +00:00
}
}
2021-05-05 01:48:16 +00:00
func DomainHandler ( c * gin . Context ) {
// Get a valid user
user := myUser ( c )
if user == nil {
c . AbortWithStatusJSON ( http . StatusUnauthorized , gin . H { "errmsg" : "User not defined." } )
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
domain , err := storage . MainStore . GetDomainByDN ( user , c . Param ( "domain" ) )
if err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Domain not found" } )
return
}
// If source is provided, check that the domain is a parent of the source
var source * happydns . SourceMeta
if src , exists := c . Get ( "source" ) ; exists {
source = & src . ( * happydns . SourceCombined ) . SourceMeta
} else if src , exists := c . Get ( "sourcemeta" ) ; exists {
source = src . ( * happydns . SourceMeta )
}
2021-05-21 11:08:18 +00:00
if source != nil && source . Id != domain . IdProvider {
2021-05-05 01:48:16 +00:00
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Domain not found (not child of source)" } )
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
c . Set ( "domain" , domain )
c . Next ( )
2020-04-22 08:17:16 +00:00
}
2020-06-24 15:33:34 +00:00
type apiDomain struct {
Id int64 ` json:"id" `
IdUser int64 ` json:"id_owner" `
IdSource int64 ` json:"id_source" `
DomainName string ` json:"domain" `
ZoneHistory [ ] happydns . ZoneMeta ` json:"zone_history" `
}
2021-05-05 01:48:16 +00:00
func GetDomain ( c * gin . Context ) {
domain := c . MustGet ( "domain" ) . ( * happydns . Domain )
2020-06-24 15:33:34 +00:00
ret := & apiDomain {
2021-05-05 01:48:16 +00:00
Id : domain . Id ,
IdUser : domain . IdUser ,
2021-05-21 11:08:18 +00:00
IdSource : domain . IdProvider ,
2021-05-05 01:48:16 +00:00
DomainName : domain . DomainName ,
2020-06-24 15:33:34 +00:00
ZoneHistory : [ ] happydns . ZoneMeta { } ,
}
2021-05-05 01:48:16 +00:00
for _ , zm := range domain . ZoneHistory {
2020-06-24 15:33:34 +00:00
zoneMeta , err := storage . MainStore . GetZoneMeta ( zm )
if err != nil {
2021-05-05 01:48:16 +00:00
log . Println ( "%s: An error occurs in getDomain, when retrieving a meta history: %w" , c . ClientIP ( ) , err )
} else {
ret . ZoneHistory = append ( ret . ZoneHistory , * zoneMeta )
2020-06-24 15:33:34 +00:00
}
}
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , ret )
}
func delDomain ( c * gin . Context ) {
if err := storage . MainStore . DeleteDomain ( c . MustGet ( "domain" ) . ( * happydns . Domain ) ) ; err != nil {
log . Printf ( "%s was unable to DeleteDomain: %w" , c . ClientIP ( ) , err )
2021-05-21 11:08:18 +00:00
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : fmt . Sprintf ( "Unable to delete your domain: %s" , err . Error ( ) ) } )
2021-05-05 01:48:16 +00:00
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , true )
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
func axfrDomain ( c * gin . Context ) {
user := c . MustGet ( "LoggedUser" ) . ( * happydns . User )
domain := c . MustGet ( "domain" ) . ( * happydns . Domain )
2021-05-21 11:08:18 +00:00
source , err := storage . MainStore . GetSource ( user , domain . IdProvider )
2020-04-22 08:17:16 +00:00
if err != nil {
2021-05-21 11:08:18 +00:00
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : fmt . Sprintf ( "Unable to find your provider: %s" , err . Error ( ) ) } )
2021-05-05 01:48:16 +00:00
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
rrs , err := source . ImportZone ( domain )
2020-04-22 08:17:16 +00:00
if err != nil {
2021-05-05 01:48:16 +00:00
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
2020-04-22 08:17:16 +00:00
}
2020-08-21 11:52:59 +00:00
var ret [ ] serviceRecord
2020-04-22 08:17:16 +00:00
for _ , rr := range rrs {
2020-08-21 11:52:59 +00:00
ret = append ( ret , serviceRecord {
String : rr . String ( ) ,
Fields : & rr ,
2020-04-22 08:17:16 +00:00
} )
}
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , ret )
2020-04-22 08:17:16 +00:00
}
type uploadedRR struct {
RR string ` json:"string" `
}
2021-05-05 01:48:16 +00:00
func prepareRR ( next func ( c * gin . Context , source * happydns . SourceCombined , domain * happydns . Domain , rr dns . RR ) ) gin . HandlerFunc {
return func ( c * gin . Context ) {
user := c . MustGet ( "LoggedUser" ) . ( * happydns . User )
domain := c . MustGet ( "domain" ) . ( * happydns . Domain )
2020-04-22 08:17:16 +00:00
2021-05-05 01:48:16 +00:00
var urr uploadedRR
err := c . ShouldBindJSON ( & urr )
if err != nil {
log . Printf ( "%s sends invalid RR JSON: %w" , c . ClientIP ( ) , err )
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : fmt . Sprintf ( "Something is wrong in received data: %w" , err ) } )
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
rr , err := dns . NewRR ( fmt . Sprintf ( "$ORIGIN %s\n$TTL %d\n%s" , domain . DomainName , 3600 , urr . RR ) )
if err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : fmt . Sprintf ( "There is an error in the given RR: %w" , err ) } )
return
2020-04-22 08:17:16 +00:00
}
2021-05-21 11:08:18 +00:00
source , err := storage . MainStore . GetSource ( user , domain . IdProvider )
2021-05-05 01:48:16 +00:00
if err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : fmt . Sprintf ( "Unable to find the required source: %w" , err ) } )
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
next ( c , source , domain , rr )
2020-04-22 08:17:16 +00:00
}
}
2021-05-05 01:48:16 +00:00
func addRR ( c * gin . Context , source * happydns . SourceCombined , domain * happydns . Domain , rr dns . RR ) {
err := source . AddRR ( domain , rr )
2020-04-22 08:17:16 +00:00
if err != nil {
2021-05-05 01:48:16 +00:00
log . Printf ( "%c: Unable to AddRR: %s" , c . ClientIP ( ) , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : fmt . Sprintf ( "Sorry, we are unable to add the RR. Please retry or try later..." ) } )
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , serviceRecord {
String : rr . String ( ) ,
Fields : & rr ,
} )
}
2020-04-22 08:17:16 +00:00
2021-05-05 01:48:16 +00:00
func delRR ( c * gin . Context , source * happydns . SourceCombined , domain * happydns . Domain , rr dns . RR ) {
err := source . DeleteRR ( domain , rr )
2020-04-22 08:17:16 +00:00
if err != nil {
2021-05-05 01:48:16 +00:00
log . Printf ( "%c: Unable to DelRR: %s" , c . ClientIP ( ) , err )
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : fmt . Sprintf ( "Sorry, we are unable to delete the RR. Please retry or try later..." ) } )
return
2020-04-22 08:17:16 +00:00
}
2021-05-05 01:48:16 +00:00
c . JSON ( http . StatusOK , true )
2020-04-22 08:17:16 +00:00
}